Bash capturing output of find with exclusion into the While loop - bash

I made a script that looks for the content of a folder recursively, excluding some paths, then asks for an action to take on each line of the results.
The find command on its own is working fine and exclude paths as expected. It looks something like that:
$SOURCE="FOLDER/"
$EXCLUDESTRING="! -path \"FOLDER/*/.svn/*\" ! -path \"FOLDER/uploads/*\" ! -path \"FOLDER/ai-cache/*\""
find "$SOURCE"* $EXCLUDESTRING # uploads and ai-cache folders are not included in the results
But when I pipe the result to the While loop it does not consider the exclusions.
find "$SOURCE"* $EXCLUDESTRING -print0 | while read -d $'\0' file_1
do
echo $file_1 # uploads and ai-cache folders are included in the results
if statement ...
more commands ...
done
I want to mention that the goal is to find the desired files and folders and process them instantaneously without using an array.
UPDATE
For those who are interested in my script (Step by step unidirectional sync) or could test (it will be very appreciated) Here is a more detailed copy:
#!/bin/bash
excludepath=( "*/.svn/*" "uploads/*" "design/*" "ai-cache/*" )
bold=`tput bold`
normal=`tput sgr0`
validsource="false"
while [ "$validsource" == "false" ]
do
echo ""
echo "Specify project to compare :"
echo -n "/home/myaccount/public_html/projects/"
read -e project
project=`echo "$project" | sed -e "s/\/*$//" `
projectpath="/home/myaccount/public_html/projects/$project"
source="$(readlink -f $projectpath)/"
if [ -d "$source" ];then
validsource="true"
else
echo "The working copy cannot be found ($projectpath)."
fi
done
echo "Compare project with folder :"
read -e target
excludestring=""
for i in "${excludepath[#]}"
do
excludestring="$excludestring ! -path \"$source$i\""
done
echo ""
echo "______________________________________________"
echo ""
echo "COMPARISON IN PROGRESS ..."
echo "______________________________________________"
echo ""
echo "List of paths excluded from the comparison: ${excludepath[#]}"
echo "Executed command : find \"$source\"* $excludestring"
echo ""
liveexclude=()
find "$source"* $excludestring -print0 | while read -d $'\0' file_1
do
file=$( echo "$file_1" | sed "s,$source,,g" ) # Strip base path
file_2=$( echo "$file_1" | sed "s,$source,$target,g" ) # Getting file path in $target
dir=$( dirname "$file_2" | sed "s,$target,,g" )
dir_1=$( dirname "$file_1" )
dir_2=$( dirname "$file_2" )
#Check for live excluded folders
process="true"
for i in "${liveexclude[#]}"
do
if [[ $file_1 == "$i"* ]]
then
process="false"
break
fi
done
if [ "$process" == "true" ];then
if [ -d "$file_1" ];then
if [ ! -d "$file_2" ] # Checking if sub-dir exists in $target
then
while [ "$confirm" != "y" ] && [ "$confirm" != "n" ]
do
echo ""
echo "${bold}Folder${normal} \"$file\" doesn't exist."
echo -n "Would you like to ${bold}create it and its entire contents${normal} ? (y/n) "
read -e confirm </dev/tty
done
if [ "$confirm" == "y" ];then
mkdir -p $file_2 # Creating if sub-dir missing
cp -r "$file_1/"* "$file_2"
fi
confirm=""
liveexclude+=("$file_2")
fi
else
if [ -f "$file_1" ];then
if [ -f "$file_2" ] # Checking if file exists in $target
then
cksum_file_1=$( cksum "$file_1" | cut -f 1 -d " " ) # Get cksum of file in $source
cksum_file_2=$( cksum "$file_2" | cut -f 1 -d " " ) # Get cksum of file in $target
if [ $cksum_file_1 -ne $cksum_file_2 ] # Check if cksum matches
then
while [ "$confirm" != "y" ] && [ "$confirm" != "n" ]
do
if [ "$file_1" -nt "$file_2" ]
then
echo ""
echo "${bold}File${normal} \"$file\" is not updated."
echo -n "Would you like to ${bold}replace${normal} it ? (y/n) "
else
echo ""
echo "${bold}File${normal} \"$file\" was modified."
echo "${bold}CAUTION${normal}: The file \"$file_2\" is newer than the file \"$file_1\""
echo -n "Would you still ${bold}overwrite${normal} it ? (y/n) "
fi
read -e confirm </dev/tty
done
if [ "$confirm" == "y" ];then
cp "$file_1" "$file_2" # Copy if cksum mismatch
fi
confirm=""
fi
else
while [ "$confirm" != "y" ] && [ "$confirm" != "n" ]
do
echo ""
echo "${bold}File${normal} \"$file\" doesn't exist."
echo -n "Would you like to ${bold}copy${normal} it ? (y/n) "
read -e confirm </dev/tty
done
if [ "$confirm" == "y" ];then
cp "$file_1" "$file_2" # Copy if file does not exist.
fi
confirm=""
fi
fi
fi
fi
done
PS. We use this script for applying new changes on an existing project if a detailed check is required.

Don't put your commands in a string, but in an array. And don't use a dollar in the left-hand side of an assignment (we're not in Perl/PHP). Oh, and avoid using upper case variable names. It looks ugly; it seems you're shouting the variable's name; but more seriously it might clash with reserved names (like PATH, LINES, GROUPS, USERS, etc.); if you stick to lower case variable names, you're on the safe side (and it's prettier!).
source=FOLDER/
excludeary=( \! -path "FOLDER/*/.svn/*" \! -path "FOLDER/uploads/*" \! -path "FOLDER/ai-cache/*" )
find "$source" "${excludeary[#]}" -print0 | while IFS= read -r -d '' file_1
do
echo "$file_1" # uploads and ai-cache folders are included in the results
if statement ...
more commands ...
done
Edit. Here's a small example:
$ mkdir Test
$ cd Test
$ mkdir -p {excl,incl}/{1,2}
$ touch {excl,incl}/{1,2}/{a,b}
$ tree
.
|-- excl
| |-- 1
| | |-- a
| | `-- b
| `-- 2
| |-- a
| `-- b
`-- incl
|-- 1
| |-- a
| `-- b
`-- 2
|-- a
`-- b
6 directories, 8 files
$ source=~/Test
$ excludeary=( \! -path "$source/excl/*" )
$ find "$source" "${excludeary[#]}"
/home/gniourf/Test
/home/gniourf/Test/excl
/home/gniourf/Test/incl
/home/gniourf/Test/incl/1
/home/gniourf/Test/incl/1/a
/home/gniourf/Test/incl/1/b
/home/gniourf/Test/incl/2
/home/gniourf/Test/incl/2/a
/home/gniourf/Test/incl/2/b
That's how ! -path works. See, you still have the /home/gniourf/Test/excl folder (but not its children). Maybe you want -prune instead:
$ pruneary=( \! \( -type d -name excl -prune \) )
$ find "$source" "${pruneary[#]}"
/home/gniourf/Test
/home/gniourf/Test/incl
/home/gniourf/Test/incl/1
/home/gniourf/Test/incl/1/a
/home/gniourf/Test/incl/1/b
/home/gniourf/Test/incl/2
/home/gniourf/Test/incl/2/a
/home/gniourf/Test/incl/2/b
Or to exclude all the 1 directories together with the excl directory:
$ excludeary=( \! \( -type d \( -name excl -o -path '*/1' \) -prune \) )
$ find "$source" "${excludeary[#]}"
/home/gniourf/Test
/home/gniourf/Test/incl
/home/gniourf/Test/incl/2
/home/gniourf/Test/incl/2/a
/home/gniourf/Test/incl/2/b

All of the necessary exclusions worked for me when I removed all of the double quotes and put everything in single quotes:
EXCLUDESTRING='! -path FOLDER/*/.svn/* ! -path \"FOLDER/uploads/* ! -path FOLDER/ai-cache/*'

Related

searching for file unix script

My script is as shown:
it searches for directories and provides info on that directory, however I am having trouble setting exceptions.
if [ -d "$1" ];
then
directories=$(find "$1" -type d | wc -l)
files=$(find "$1" -type f | wc -l)
sym=$(find "$1" -type l | wc -l)
printf "%s %'d\n" "Directories" $directories
printf "%s %'d\n" "Files" $files
printf "%s %'d\n" "Sym links" $sym
exit 0
else
echo "Must provide one argument"
exit 1
fi
How do I make it so that if I search for a file it tells me that a directory needs to be inputted? I'm stuck on it, I've tried test commands but I don't know what to do.
You're missing your shebang in the first line of your script:
#!/bin/bash
I get correct results from your script if I add it:
Directories 1,991
Files 13,363
Sym links 0
You may have to set the correct execution permissions also chmod +x scriptname.sh?
Entire script looks like this:
#!/bin/bash
if [ -z "$1" ];
then
echo "Please provide at least one argument!"
exit 1
elif [ -d "$1" ];
then
directories=$(find "$1" -type d | wc -l)
files=$(find "$1" -type f | wc -l)
sym=$(find "$1" -type l | wc -l)
printf "%s %'d\n" "Directories" $directories
printf "%s %'d\n" "Files" $files
printf "%s %'d\n" "Sym links" $sym
exit 0
else
echo "This is a file, not a directory"
exit 1
fi

Converting FLAC file collection to ALAC in another directory with shell script

I have searched many forums and websites to create an ALAC collection from my FLAC collection with the same directory structure with no success. Therefore I coded my own shell script and decided to share here so others can use or improve on it.
Problems I wanted to solve:
Full automation of conversion. I did not want to go and run scripts
in each and every directory.
Recursive file search
Moving all the structure from one location to another by converting flac to alac and copying the artwork. nothing else.
I did not want flac and alac files in the same directory.(which the below
script I believe can do that)
Here is how the script turned out. It works for me, I hope it does for you as well. I am using Linux Mint and bash shell.
2014-12-08 - Made some changes and now it is working fine. Before it was creating multiple copies.
Usage is: ./FLACtoALAC.sh /sourcedirectory /targetdirectory
Here are some explanations:
Source: /a/b/c/d/e/ <- e has flac
/g/f/k <- k has artwork
/l <- l has mp3
Target: /z/u/v/g/f
when the command is run : ./FLACtoALAC.sh /a/b/ /z/u/
I want the structure look like:
/z/u/v/g/f <- f was already there
/c/d/e/ <- e had flac, so created with the tree following source (/a/b)
/c/g/f/k <- k had artwork, so created with the tree following source (/a/b)
not created l <- l did not have any of the png,jpg or flac files.
I do not want to create any directory that does not contain png, jpg or flac,
unless it is a parent to one of such those directories.
Now the updated code:
#!/bin/bash
if [[ $1 ]]
then
if [[ ${1:0:1} = / || ${1:0:1} = ~ ]]
then Source_Dir=$1
elif [[ ${1:0:1} = . ]]
then Source_Dir=`pwd`
else Source_Dir=`pwd`'/'$1
fi
else Source_Dir=`pwd`'/'
fi
if [[ $2 ]]
then
if [[ ${2:0:1} = / || ${2:0:1} = ~ ]]
then Target_Dir=$2
elif [[ ${2:0:1} = . ]]
then Target_Dir=`pwd`
else Target_Dir=`pwd`'/'$2
fi
else Target_Dir=`pwd`'/'
fi
echo "Source Directory : "$Source_Dir
echo "Target Directory : "$Target_Dir
typeset -i Source_Dir_Depth
Source_Dir_Depth=`echo $Source_Dir | grep -oi "\/" | wc -l`
typeset -i Target_Dir_Depth
Target_Dir_Depth=`echo $Target_Dir | grep -oi "\/" | wc -l`
echo "Depth of the Source Directory: "$Source_Dir_Depth
echo "Depth of the Target Directory: "$Target_Dir_Depth
echo "Let's check if the Target Directory exists, if not we will create"
typeset -i Number_of_depth_checks
Number_of_depth_checks=$Target_Dir_Depth+1
for depth in `seq 2 $Number_of_depth_checks`
do
Target_Directory_Tree=`echo ${Target_Dir} | cut -d'/' -f-${depth}`
if [[ -d "$Target_Directory_Tree" ]]
then
echo "This directory exists ("$Target_Directory_Tree"), moving on"
else
Create_Directory=`echo ${Target_Dir} | cut -d'/' -f-${depth}`
echo "Creating the directory/subdirectory $Create_Directory"
mkdir -pv "$Create_Directory"
fi
done
Directory_List=`find "${Source_Dir}" -type d -exec sh -c 'ls -tr -1 "{}" | sort | egrep -iq "*.(jpg|png|flac)$"' ';' -print`
oIFS=$IFS
IFS=$'\n'
for directories in $Directory_List
do
echo "Directories coming from the source : $directories"
typeset -i directories_depth
directories_depth=`echo $directories | grep -oi "\/" | wc -l`
echo "Number of sub-directories to be checked: $Source_Dir_Depth"
typeset -i number_of_directories_depth
number_of_directories_depth=$directories_depth+1
for depth in `seq 2 $number_of_directories_depth`
do
Source_Tree=`echo ${Source_Dir} | cut -d'/' -f-${depth}`
Subdirectory_Tree=`echo ${directories} | cut -d'/' -f-${depth}`
Subdirectory_Remaining_Tree=`echo ${directories} | cut -d'/' -f${depth}-`
echo "source tree : $Source_Tree"
echo "source tree : $Subdirectory_Tree"
if [[ $depth -le $Source_Dir_Depth && $Source_Tree = $Subdirectory_Tree ]]
then
echo "Common Directory, skipping ($Subdirectory_Tree)"
continue
else
export Targetecho=$(echo $Target_Dir | sed -e 's/\r//g')
export Destination_Directory=${Targetecho}${Subdirectory_Remaining_Tree}
echo "Destination directory is : $Destination_Directory"
export Sub_directories_depth=`echo $Destination_Directory | grep -oi "\/" | wc -l`
echo "Total destination depth : $Sub_directories_depth"
echo "Now we are checking target directory structure"
fi
break
done
echo "Gettin into the new loop to verify/create target structure"
typeset -i number_of_Sub_directories_depth
number_of_Sub_directories_depth=$Sub_directories_depth+1
for subdepth in `seq 2 $number_of_Sub_directories_depth`
do
Target_Subdirectory_Tree=`echo ${Destination_Directory} | cut -d'/' -f-${subdepth}`
if [[ $subdepth < $number_of_Sub_directories_depth && -d "$Target_Subdirectory_Tree" ]]
then
echo "Directory already exists in the destination ($Target_Subdirectory_Tree)"
elif [[ $subdepth < $number_of_Sub_directories_depth && ! -d "$Target_Subdirectory_Tree" ]]
then
echo "Creating the path in the destination ($Target_Subdirectory_Tree)"
mkdir -pv "$Target_Subdirectory_Tree"
elif [[ $subdepth -eq $number_of_Sub_directories_depth ]]
then
if [[ ! -d "$Destination_Directory" ]]
then
echo "Creating Directory: $Destination_Directory"
mkdir -pv "$Destination_Directory"
fi
echo "Directory already exists in the destination ($Destination_Directory)"
#Flac file processing starts here once the directory is found
Flac_File_List=`(shopt -s nocaseglob ; ls -tr "${directories}"/*.flac | sort)`
echo "List of files in $directories :"
echo $Flac_File_List
for flac_files in $Flac_File_List
do
echo "files : $flac_files"
typeset -i flac_file_depth
flac_file_depth=`echo $flac_files | grep -oi "\/" | wc -l`
flac_file_depth=$flac_file_depth+1
echo "flac_file_depth : $flac_file_depth"
Flac_File_Name=`echo ${flac_files} | cut -d'/' -f${flac_file_depth}`
echo "Flac_File Name : $Flac_File_Name"
Destination_File=${Destination_Directory}'/'${Flac_File_Name}
echo "will convert $Flac_File_Name from $flac_files to $Destination_File"
yes | ffmpeg -i "$flac_files" -vf "crop=((in_w/2)*2):((in_h/2)*2)" -c:a alac "${Destination_File%.flac}.m4a"
done
#Artwork file processing starts here once the directory is found
Art_File_List=`(shopt -s nocaseglob ; ls -tr "${directories}"/*.{png,jpg} | sort)`
echo "List of files in $directories :"
echo $Art_File_List
for art_files in $Art_File_List
do
echo "files : $art_files"
typeset -i art_file_depth
art_file_depth=`echo $art_files | grep -oi "\/" | wc -l`
art_file_depth=$art_file_depth+1
echo "file_depth : $art_file_depth"
Art_File_Name=`echo ${art_files} | cut -d'/' -f${art_file_depth}`
echo "File Name : $Art_File_Name"
Destination_File=${Destination_Directory}'/'${Art_File_Name}
echo "will copy $Art_File_Name from $art_files to $Destination_File"
cp "$art_files" "$Destination_File"
done
else
echo "did nothing!!!"
fi
done
done
IFS=$oIFS
feel free to change, improve, distribute.
Caglar
Try this out:
#!/bin/bash
src_dir="in"
dst_dir="out"
find ${src_dir} -type f -print0|while IFS= read -r -d '' src_file; do
dst_file=${src_file/$src_dir/$dst_dir}
echo "src_file=${src_file} dst_file=${dst_file}"
mkdir -pv `dirname $dst_file`
# use above variables and run convert command with it here
done
To test how it works:
mkdir in out
cd in
mkdir 1 2 3
find . -type d -exec touch {}/foo {}/bar {}/baz \;
cd ..
./run_my_script.sh
Now you only need to attach your convert function/script/command/whatever and improve it to read src_dir and dst_dir from the command line (I would recommend man bash - > getopts)

Bash script loop through subdirectories and write to file

I have no idea I have spent a lot of hours dealing with this problem. I need to write script. Script should loop recursively through subdirectories in current directory. It should check files count in each directory. If file count is greater than 10 it should write all names of these file in file named "BigList" otherwise it should write in file "ShortList". This should look like
---<directory name>
<filename>
<filename>
<filename>
<filename>
....
---<directory name>
<filename>
<filename>
<filename>
<filename>
....
My script only works if subdirecotries don't include subdirectories in turn.
I am confused about this. Because it doesn't work as I expect. It will take less than 5 minutes to write this on any programming language for my.
Please help to solve this problem , because I have no idea how to do this.
Here is my script
#!/bin/bash
parent_dir=""
if [ -d "$1" ]; then
path=$1;
else
path=$(pwd)
fi
parent_dir=$path
loop_folder_recurse() {
local files_list=""
local cnt=0
for i in "$1"/*;do
if [ -d "$i" ];then
echo "dir: $i"
parent_dir=$i
echo before recursion
loop_folder_recurse "$i"
echo after recursion
if [ $cnt -ge 10 ]; then
echo -e "---"$parent_dir >> BigList
echo -e $file_list >> BigList
else
echo -e "---"$parent_dir >> ShortList
echo -e $file_list >> ShortList
fi
elif [ -f "$i" ]; then
echo file $i
if [ $cur_fol != $main_pwd ]; then
file_list+=$i'\n'
cnt=$((cnt + 1))
fi
fi
done
}
echo "Base path: $path"
loop_folder_recurse $path
I believe that this does what you want:
find . -type d -exec env d={} bash -c 'out=Shortlist; [ $(ls "$d" | wc -l) -ge 10 ] && out=Biglist; { echo "--$d"; ls "$d"; echo; } >>"$out"' ';'
If we don't want either to count subdirectories to the cut-off or to list them in the output, then use this version:
find . -type d -exec env d={} bash -c 'out=Shortlist; [ $(ls -p "$d" | grep -v "/$" | wc -l) -ge 10 ] && out=Biglist; { echo "--$d"; ls -p "$d"; echo; } | grep -v "/$" >>"$out"' ';'

How to find the filenames changed between to linux directories as per contents of the file using md5sum followed by diff command

I have two linux directories dir1 and dir2 with some files in both. Now i want list of filenames with files added and files deleted in dir2 as compared to dir1. The files should be compared as per the data or contents in the file. I am new to the linux bash scripting. Please help me.
Currently i am doing this like below :
find dir1 -iname *.c -o -iname *.h -o -iname *.prm | xargs -n1 md5sum > dir1.fingerprint.md5sum
find dir2 -iname *.c -o -iname *.h -o -iname *.prm | xargs -n1 md5sum > dir2.fingerprint.md5sum
cat dir1.fingerprint.md5sum | cut -d" " -f1 | sort -u > dir1.fingerprint
cat dir2.fingerprint.md5sum | cut -d" " -f1 | sort -u > dir2.fingerprint
diff -NrU 2 dir1.fingerprint dir2.fingerprint
I am getting the result as some change id's as shown below :
--- dir1.fingerprint 2013-03-08 11:57:24.421311354 +0530
+++ dir2.fingerprint 2013-03-08 11:57:34.901311856 +0530
## -1,3 +1,3 ##
-43551a78e0f5b0be4aec23fdab881e65
-4639647e4f86eb84987cd01df8245d14
4c9cc7c6332b4105197576f66d1efee7
+9f944e70cb20b275b2e9b4f0ee26141a
+d41d8cd98f00b204e9800998ecf8427e
I want the result as the filenames for files modified or added newly to dir2. How to get this. Please help me.
Try this script with the arguments dir2 and dir1
#!/bin/sh
if [ "x$1" == "x" ]
then
exit 0
fi
if [ "x$2" == "x" ]
then
exit 0
fi
#echo "DIFF $1 $2"
if [ -f $1 ]
then
if [ -e $2 ]
then
diff $1 $2 >/dev/null
if [ "$?" != "0" ]
then
echo "DIFFERENT $1"
fi
fi
exit 0
fi
if [ "x`ls $1`" != "x" ]
then
for f in `ls $1`
do
$0 $1/$f $2/$f
done
fi
exit 0
EDIT:
if [ "x`ls $1`" != "x" ]
then
for f in `ls $1`
do
if [ -f $1/$f ]
then
for g in `ls $2`
do
if [ -f $2/$g ]
then
diff $1/$f $2/$g >/dev/null
if [ "$?" == "0" ]
then
echo "SAME CONTENT $1/$f $2/$g"
fi
fi
done
fi
done
fi

Need a quick bash script

I have about 100 directories all in the same parent directory that adhere to the naming convention [sitename].com. I want to rename them all [sitename].subdomain.com.
Here's what I tried:
for FILE in `ls | sed 's/.com//' | xargs`;mv $FILE.com $FILE.subdomain.com;
But it fails miserably. Any ideas?
Use rename(1).
rename .com .subdomain.com *.com
And if you have a perl rename instead of the normal one, this works:
rename s/\\.com$/.subdomain.com/ *.com
Using bash:
for i in *
do
mv $i ${i%%.com}.subdomain.com
done
The ${i%%.com} construct returns the value of i without the '.com' suffix.
What about:
ls |
grep -Fv '.subdomain.com' |
while read FILE; do
f=`basename "$FILE" .com`
mv $f.com $f.subdomain.com
done
See: http://blog.ivandemarino.me/2010/09/30/Rename-Subdirectories-in-a-Tree-the-Bash-way
#!/bin/bash
# Simple Bash script to recursively rename Subdirectories in a Tree.
# Author: Ivan De Marino <ivan.demarino#betfair.com>
#
# Usage:
# rename_subdirs.sh <starting directory> <new dir name> <old dir name>
usage () {
echo "Simple Bash script to recursively rename Subdirectories in a Tree."
echo "Author: Ivan De Marino <ivan.demarino#betfair.com>"
echo
echo "Usage:"
echo " rename_subdirs.sh <starting directory> <old dir name> <new dir name>"
exit 1
}
[ "$#" -eq 3 ] || usage
recursive()
{
cd "$1"
for dir in *
do
if [ -d "$dir" ]; then
echo "Directory found: '$dir'"
( recursive "$dir" "$2" "$3" )
if [ "$dir" == "$2" ]; then
echo "Renaming '$2' in '$3'"
mv "$2" "$3"
fi;
fi;
done
}
recursive "$1" "$2" "$3"
find . -name '*.com' -type d -maxdepth 1 \
| while read site; do
mv "${site}" "${site%.com}.subdomain.com"
done
Try this:
for FILE in `ls -d *.com`; do
FNAME=`echo $FILE | sed 's/\.com//'`;
`mv $FILE $FNAME.subdomain.com`;
done

Resources