`cd` into a directory using a pattern - regex

I am trying to cd into a directory which is named with an ip address(eg: 10.0.10.10). The name of the folder changes as the ip address of the node changes. I want to have dynamic cd command to cd into that folder. cd ~/mnt/<ip address pattern>
It works if I use cd ~/mnt/1* or any other similar wildcard operator is used. I want it to be worked with the pattern [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}

If your find supports -regex (you may want to add -regextype with GNU find systems or -E on BSD find to enable ERE syntax):
re='(^|/)[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'
IFS= read -r -d '' dirname < <(find /mnt -maxdepth 1 -type d -regex "$re" -print0)
[[ $dirname ]] && cd "$dirname"
...or you can just use native bash:
re='(^|/)[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'
for dir in /mnt/*; do
[[ $dir =~ $re ]] && { cd "$dir" && break; }
done

Related

Linking directories inside directories in Bash

My script processes files.lst and it has a loop that looks like this
while read src_column dest_column; do
if [[ -d $src ]]; then
src="../../default/$src_column/*"
else
src="../../default/$src_column"
fi
pushd $dest
ln -s $src .
popd
done < files.lst
files.lst
#~source~ ~destination~
data dir1
default/def1.txt new1.txt
data dir2/dir22/dir222
default/def1.txt dir2/dir22/dir222/new1.txt
default dir2/dir22
default/def2.txt dir2/dir22/ne2.txt
The cases should be like this:
if destinations are dir2/dir22/dir222 or dir2/dir22/dir222/new1.txt
the starting prefix of $src should be ../../../../default
if destinations are dir2/dir22 or dir2/dir22/new2.txt
the starting prefix of $src should be ../../../default
if destinations are dir2 or dir2/new2.txt
the starting prefix of $src should be ../../default
The problem is I don't know how I will count the directories how deep they are. What approach should I do? I am thinking of regex but I got no idea how I'll use it.
Using sed to calculate the paths...:
while read src_column dest_column; do
if [[ -d $src ]]; then
dest_column="$dest_column/"
fi
src_prefix="$(sed -r 's|/[^/]*$|/|; s|//*|/|g; s|[^/]+|..|g' <<< "./$dest_column")default"
# sed command details:
# First expression: strip out any file.txt from $dest_column
# 2nd expression: Change duplicate / to single / (e.g. a/b//c// to a/b/c
# Last expression: Change any path to `..`
#Finally append the missing ../default.
if [[ -d $src ]]; then
src="$src_prefix/$src_column/*"
else
src="$src_prefix/$src_column"
fi
pushd $dest
ln -s $src .
popd
done < files.lst

find works in terminal but fails in bash script

So I'm writing a bash script that counts the number of files in a directory and outputs a number. The function takes a directory argument as well as an optional file-type extension argument.
I am using the following lines to set the dir variable to the directory and ext variable to a regular expression that will represent all the file types to count.
dir=$1
[[ $# -eq 2 ]] && ext="*.$2" || ext="*"
The problem I am encountering occurs when I attempt to run the following line:
echo $(find $dir -maxdepth 1 -type f -name $ext | wc -l)
Running the script from the terminal works when I provide the second file-type argument but fails when I don't.
harrison#Luminous:~$ bash Documents/howmany.sh Documents/ sh
3
harrison#Luminous:~$ bash Documents/howmany.sh Documents/
find: paths must precede expression: Desktop
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
0
I have searched for this error and I know it's an issue with the shell expanding my wildcard as explained here. I've tried experimenting with single quotes, double quotes, and backslashes to escape the asterisk but nothing seems to work. What's particularly interesting is that when I try running this directly through the terminal, it works perfectly fine.
harrison#Luminous:~$ echo $(find Documents/ -maxdepth 1 -type f -name "*" | wc -l)
6
Simplified:
dir=${1:-.} #if $1 not set use .
name=${2+*.$2} #if $2 is set use *.$2 for name
name=${name:-*} #if name still isnt set, use *
find "$dir" -name "$name" -print #use quotes
or
name=${2+*.$2} #if $2 is set use *.$2 for name
find "${1:-.}" -name "${name:-*}" -print #use quotes
also, as #John Kugelman says, you could use:
name=${2+*.$2}
find "${1:-.}" ${name:+-name "$name"} -print
find . -name "*" -print is the same as find . -print, so if $name isn't set, there's no need to specify -name "*".
Try this:
dir="$1"
[[ $# -eq 2 ]] && ext='*.$2' || ext='*'
If that doesn't work, you can just switch to an if statement, where you use the -name pattern in a branch and you don't in the other.
A couple more points:
Those are not regular expressions, but rather shell patterns.
echo $(command) is just equivalent to command.

Shell script to rename multiple files from their parent folders

I'm looking for a script for below structure:
Before :
/Description/TestCVin/OpenCVin/NameCv/.....
/Description/blacVin/baka/NameCv_hubala/......
/Description/CVintere/oldCvimg/NameCv_add/.....
after:
/Description/TestaplCVin/OpenaplCVin/NameaplCv/.....
/Description/blaapcVlin/baka/NameaplCv_hubala/......
/Description/aplCVintere/oldaplCvimg/NameaplCv_add/.....
I want to rename " Cv or CV or cV " >> "aplCv or aplCV or aplcV" in all folder by regular expression...
My script does look like:
#!/bin/sh
printf "Input your Directory path: -> "
read DIR
cd "$DIR"
FILECASE=$(find . -iname "*cv*")
LAST_DIR_NAME=""
for fdir in $FILECASE
do
if [[ -d $fdir ]];
then
LAST_DIR_NAME=$fdir
fi
FILE=$(echo $fdir | sed -e "s/\([Cc][Vv]\)/arpl\1/g")
echo "la file $FILE"
if ([[ -f $fdir ]] && [[ "$fdir" =~ "$LAST_DIR_NAME" ]]);
then
FILECASE=$(find . -iname "*cv*")
tmp=$(echo $LAST_DIR_NAME | sed -e "s/\([Cc][Vv]\)/arpl\1/g")
fdir=$(echo $fdir | sed -e 's|'$LAST_DIR_NAME'|'$tmp'|g')
fi
mv -- "$fdir" "$FILE"
done
But it throws an error ..:(
How could I write it to rename the files according to their folder names?
You can do like this
#!/bin/sh
printf "Input your Directory path: -> "
read DIR
cd "$DIR"
MYARRAY=$(find . -iname "*cv*" )
touch "tmpfile"
for fdir in $MYARRAY
do
echo "$fdir" >> "tmpfile"
done
MYARRAY=$(tac "tmpfile")
for fdir in $MYARRAY
do
cd "$fdir"
prev=$(cd -)
base=$(basename $fdir)
cd ..
nDIR=$(echo "$base" | sed -e "s/\([Cc][Vv]\)/arpl\1/g")
mv "$base" "$nDIR"
cd $prev
done
rm -f "tmpfile"
Also one issue i think tac command not included in Mac OS X.Instead tac use tail -r like MYARRAY=$(tail -r "tmpfile")
Always make a backup before playing with this kind of scripts.
You can try the following:
find . -iname '*cv*' -exec echo 'mv {} $(echo $(dirname {})/$(basename {}|sed s/cv/apl/gi))' \;|tac|xargs -i bash -c 'eval {}'
This uses -exec to print commands for renaming.
The second arguments are generated by using shell substitutions to replace cv with apl in the last part of the path.
tac is used to reverse the order of the commands, so that we do not rename a directory before working with its contents.
Finally, we eval the commands with bash.
Also, do not use -exec in a permanent script. Please read the security warnings about exec in the find man-page.

Use datestring in a filename to create folder directory and move files

The script I'm trying to pull of should move files to a destination folder and place them in "year/month/" folders according to the files name which starts with YYYY-MM-DD.
Example:
2013-08-03-image_name.png -> ~/B/uploads/2013/08/2013-08-03-image_name.png
2012-01-01-image_name.png -> ~/B/uploads/2012/01/2012-01-01-image_name.png
Plan of action
(1) Set path variables
source=~/Desktop/A/
targetPath=~/Desktop/B/uploads/
(2) Perform these actions on each file in $source
cd "$source";
for i in *.png
do
# STEP 3
# STEP 4
done
(3) Step 3: Image Optimization √
(4) Step 4: File away files to directory that machtes datename
(4a) Search for datestring in filename via ^(\d{4})-(\d{2}) and create $datePath, c.f. datePath=2013/08/. I image this something like this…
awk -F … somehow put the regex here with a search and replace "-" into "/"
and save it as a variable.
(4b) Create new target directory if it doesn't exist and move files there.
targetDir=$targetPath$datePath
mkdir -p $targetDir
mv -v "$i" "$destination"
PS: Bash would be nice.
I am providing you solution for finding target path for your files in pure BASH:
f='2013-08-03-image_name.png'
targetPath=~/Desktop/B/uploads/
[[ "$f" =~ ^([0-9]{4})-([0-9]{2}) ]] && \
echo "$targetPath${BASH_REMATCH[1]}/${BASH_REMATCH[2]}/$f"
OUTPUT:
~/Desktop/B/uploads/2013/08/2013-08-03-image_name.png
I'd use find + egrep to filter, then sed to build the name of the destination directory.
cd /src
IMAGES=`find . -type f -name '*.png' -print | egrep '^./[0-9]{4}-[0-9]{2}-[0-9]{2}-.+.png$'`
for IMG in $IMAGES; do
# optimize here
DIR=`echo $IMG | sed -E 's/^\.\/([0-9]{4})-([0-9]{2})-[0-9]{2}-.+.png/\1\/\2/'`
mkdir -p /dest/$DIR
mv /src/$IMG /dest/$DIR/
done
I think you will find glob useful and might find some inspiration in this question
Here's another bash solution, without using a regex/match:
srcdir=<whatever>
destdir=<whatever>
cd "${srcdir}"
for f in *-*-*-*.png
do
{ IFS=- read y m rest
[[ -d "${destdir}/${y}/${m}" ]] || mkdir -p "${destdir}/${y}/${m}"
echo mv "${f}" "${destdir}/${y}/${m}/${f}"
} <<< "${f}"
done
The for f in ... pattern may need some adjusting, depending on what other stuff you have in your source directory...
Remove the echo from in front of mv if you're satisfied with the proposed set of commands the above produces (or just pipe the whole thing into a subshell .... | bash).

Determining correct extension in bash with Regular Expressions

Was wondering if someone could help me out with regular expressions and bash.
I'm trying to execute a set of commands on files that only have a certain extension, in this case: mpg, mpeg, avi, and mkv.
I've actually found a solution here, however, it doesn't seem to work. If someone can tell me why, I'd appreciate it.
#!/bin/bash
# Configuration
TARGETDIR="$1"
TARGETEXT="(mpg|mpeg|avi|mkv)"
for d in `find $1 -type d`
do
echo "Searching directory: $d"
for f in "$d"/*
do
if [ -d "${f}" ];
then
# File is a directory, do not perform
echo "$f is a directory, not performing ..."
elif [ -f "${f}" ];
then
filename=$(basename "$f")
extension="${filename##*.}"
if [ "$extension" == "$TARGETEXT" ];
then
echo "Match"
else
echo "Mismatch - $f - $extension"
fi
fi
done
done
Again, any assistance is appreciated.
This can probably be done using only the find command.
find $TARGETDIR -regex ".*\\.$TARGETEXT" -type f -exec your_command {} \;
Instead of direct string comparison
if [ "$extension" == "$TARGETEXT" ];
use Bash regex matching syntax
if [[ "$extension" =~ $TARGETEXT ]];
Note the double [[ ]] and the non-quoted $TARGETEXT.
You can do this in bash without regular expressions, just file patterns:
shopt -s globstar nullglob
for f in **/*.{mpg,mpeg,avi,mkv}; do
if [[ -f "$f" ]]; then
# do something with the file:
echo "$f"
fi
done