Bash: Creating subdirectories reading from a file - bash

I have a file that contains some keywords and I intend to create subdirectories into the same directory of the same keyword using a bash script. Here is the code I am using but it doesn't seem to be working.
I don't know where I have gone wrong. Help me out
for i in `cat file.txt`
do
# if [[ ! -e $path/$i ]]; then
echo "creating" $i "directory"
mkdir $path/$i
# fi
grep $i file >> $path/$i/output.txt
done
echo "created the files in "$path/$TEMP/output.txt

You've gone wrong here, and you've gone wrong here.
while read i
do
echo "Creating $i directory"
mkdir "$path/$i"
grep "$i" file >> "$path/$i"/output.txt
done < file.txt
echo "created the files in $path/$TEMP/output.txt"

78mkdir will refuse to create a directory, if parts of it do not exist.
e.g. if there is no /foo/bar directory, then mkdir /foo/bar/baz will fail.
you can relax this a bit by using the -p flag, which will create parent directories if necessary (in the example, it might create /foo and /foo/bar).
you should also use quotes, in case your paths contain blanks.
mkdir -p "${path}/${i}"
finally, make sure that you are actually allowed to create directories in $path

Related

Looping through files with bashscript not working

I have this code which works:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
for entry in "$DIR"/*
do
echo "$entry"
done
But when I try to do this,
for entry in "$DIR/Images"/*
do
echo "$entry"
done
then my script tries to access a file with the name $DIR/Images/*
When I do this in attempt to fix the problem:
for entry in "$(DIR/Images)"/*
do
echo "$entry"
done
the script prints out all files in the root directory of my mac. How Do I make my script loop through all files in $DIR/Images/?
assuming of course that Images exists
for entry in "$DIR"/Images/*
works, per your comment.
I'm not convinced there is a difference between "$DIR/Images"/* and "$DIR"/Images/*.
for entry in "$DIR/Images/*"
Will not work, because asterisks are not expanded in quotes, and $DIR/Images/* will not work if $DIR contains spaces.
However,
mkdir "folder with spaces"
a="folder with spaces"
mkdir "$a"/bc
touch "$a"/bc/def.txt
echo "$a"/bc/*
>> folder with spaces/bc/def.txt
echo "$a/bc"/*
>> folder with spaces/bc/def.txt
I.e., both ways work on my machine.

Bash: Find any subdirectories without a given file present

I want to know if my file exists in any of the sub directories below. The sub directories are created in the steps above in my shell script, the below code always tells me the file do not exist (even if it does) and I want the path to be printed as well.
#!/bin/bash
....
if ! [[ -e [ **/**/somefile.txt && -s **/**/somefile.txt ]]; then
echo "===> Warn: somefile.txt was not created in the following path: "
# I want to be able to print the path in which file is not generated
exit 1
fi
I know the file name is somefile.txt which is to be created in all sub-directories, but the subdirectory names change a lot.. Hence globbing.
#!/bin/bash
shopt -s extglob ## enable **, which by default has no special behavior
for d in **/; do
if ! [[ -s "$d/somefile.txt" ]]; then
echo "===> WARN: somefile.txt was not created (or is empty) in $d" >&2
exit 1
fi
done

Passing a path as an argument to a shell script

I've written bash script to open a file passed as an argument and write it into another file. But my script will work properly only if the file is in the current directory. Now I need to open and write the file that is not in the current directory also.
If compile is the name of my script, then ./compile next/123/file.txt should open the file.txt in the passed path. How can I do it?
#!/bin/sh
#FIRST SCRIPT
clear
echo "-----STARTING COMPILATION-----"
#echo $1
name=$1 # Copy the filename to name
find . -iname $name -maxdepth 1 -exec cp {} $name \;
new_file="tempwithfile.adb"
cp $name $new_file #copy the file to new_file
echo "compiling"
dir >filelist.txt
gcc writefile.c
run_file="run_file.txt"
echo $name > $run_file
./a.out
echo ""
echo "cleaning"
echo ""
make clean
make -f makefile
./semantizer -da <withfile.adb
Your code and your question are a bit messy and unclear.
It seems that you intended to find your file, given as a parameter to your script, but failed due to the maxdepth.
If you are given next/123/file.txt as an argument, your find gives you a warning:
find: warning: you have specified the -maxdepth option after a
non-option argument -iname, but options are not positional (-maxdepth
affects tests specified before it as well as those specified after
it). Please specify options before other arguments.
Also -maxdepth gives you the depth find will go to find your file until it quits. next/123/file.txt has a depth of 2 directories.
Also you are trying to copy the given file within find, but also copied it using cp afterwards.
As said, your code is really messy and I don't know what you are trying to do. I will gladly help, if you could elaborate :).
There are some questions that are open:
Why do you have to find the file, if you already know its path? Do you always have the whole path given as an argument? Or only part of the path? Only the basename ?
Do you simply want to copy a file to another location?
What does your writefile.c do? Does it write the content of your file to another? cp does that already.
I also recommend using variables with CAPITALIZED letters and checking the exit status of used commands like cp and find, to check if these failed.
Anyway, here is my script that might help you:
#!/bin/sh
#FIRST SCRIPT
clear
echo "-----STARTING COMPILATION-----"
echo "FILE: $1"
[ $# -ne 1 ] && echo "Usage: $0 <file>" 1>&2 && exit 1
FILE="$1" # Copy the filename to name
FILE_NEW="tempwithfile.adb"
cp "$FILE" "$FILE_NEW" # Copy the file to new_file
[ $? -ne 0 ] && exit 2
echo
echo "----[ COMPILING ]----"
echo
dir &> filelist.txt # list directory contents and write to filelist.txt
gcc writefile.c # ???
FILE_RUN="run_file.txt"
echo "$FILE" > "$FILE_RUN"
./a.out
echo
echo "----[ CLEANING ]----"
echo
make clean
make -f makefile
./semantizer -da < withfile.adb

Shell Script to copy all files in a directory to a specified folder

I'm new in shell script and I am trying to figure out a way to write a script that copies all the files in the current directory to a directory specified from a .txt file and if there are matching names, it adds the current date in the form of FileName_YYYYMMDDmmss to the name of the file being copied to prevent overwritting.
Can someone help me out?
I saw thinking something along the lines of
#!/bin/bash
source=$pwd #I dont know wheter this actually makes sense I just want to
#say that my source directory is the one that I am in right now
destination=$1 #As I said I want to read the destination off of the .txt file
for i in $source #I just pseudo coded this part because I didn't figure it out.
do
if(file name exists)
then
copy by changing name
else
copy
fi
done
the problem is I have no idea how to check whether the name exist and copy and rename at the same time.
Thanks
How about this? I am supposing that the target directory is in the
file new_dir.txt.
#!/bin/bash
new_dir=$(cat new_dir.txt)
now=$(date +"%Y%m%d%M%S")
if [ ! -d $new_dir ]; then
echo "$new_dir doesn't exist" >&2
exit 1
fi
ls | while read ls_entry
do
if [ ! -f $ls_entry ]; then
continue
fi
if [ -f $new_dir/$ls_entry ]; then
cp $ls_entry $new_dir/$ls_entry\_$now
else
cp $ls_entry $new_dir/$ls_entry
fi
done
I guess this what you are looking for :
#!/bin/bash
dir=$(cat a.txt)
for i in $(ls -l|grep -v "^[dt]"|awk '{print $9}')
do
cp $i $dir/$i"_"$(date +%Y%m%d%H%M%S)
done
I assumed that a.txt contains only the name of the destination directory. If there are other entries, you should add some filter to the first statement(using grep or awk).
NB: I used full time stamp(YYYYMMDDHHmmss) instead of your YYYYMMDDmmss as it doesn't seem logical.

Removing old directories with logs

My IM stores the logs according to the contact name. I have created a file with the list of active contacts. My problem is following:
I would like to create a bash script with read the active contacts names from the file and compare it with the directories. If the directory name wouldn't be found on the list, it would be moved to another directory (let's call it "archive"). I try to visualise it for you.
content of the list:
contact1
contact2
content of the dir
contact1
contact2
contact3
contact4
after running of the script, the content fo the dir:
contact1
contact2
contact3 ==> ../archive
contact4 ==> ../archive
You could use something like this:
mv $(ls | grep -v -x -F -f ../file.txt) ../archive
Where ../file.txt contains the names of the directories that should not be moved. It is assumed here that the current directory only contains directories, if that is not the case, ls should be replaced with something else. Note that the command fails if there are no directories that should be moved.
Since in the comments to the other answer you state that directories with whitespace in the name can occur, you could replace this by:
for i in *
do
echo $i | grep -v -x -q -F -f ../file.txt && mv "$i" ../archive
done
This is an improved version of marcog's answer. Note that the associative array requires Bash 4.
#!/bin/bash
sourcedir=/path/to/foo
destdir=/path/to/archive
contactfile=/path/to/list
declare -A contacts
while read -r contact
do
contacts[$contact]=1
done < "$contactfile"
for contact in "$sourcedir"/*
do
if [[ -f $contact ]]
then
index=${contact##*/}
if [[ ! ${contacts[$index]} ]]
then
mv "$contact" "$destdir"
fi
fi
done
Edit:
If you're moving directories instead of files, then change the for loop above to look like this:
for contact in "$sourcedir"/*/
do
index=${contact/%\/}
index=${index##*/}
if [[ ! ${contacts[$index]} ]]
then
mv "$contact" "$destdir"
fi
done
There might be a more concise solution, but this works. I'd strongly recommend prefixing the mv with echo to test it out first, otherwise you could end up with a serious mess if it doesn't do what you want.
declare -A contacts
for contact in "$#"
do
contacts[$contact]=1
done
ls a | while read contact
do
if [[ ! ${contacts[$contact]} ]]
then
mv "a/$contact" ../archive
fi
done

Resources