Having trouble moving files in bash - linux

#! /bin/bash
for i in {0..9} ;
do
mkdir -p "d$i "
for j in {0..9};
do
if [ -e "./f$i$j.txt" ];
then
echo 'Moving!'
mv "./f$i$j.txt" "./d$i/f$j.txt"
fi
done
done
The above code is intended to search the current working directory for any files of name f##.txt where # is a number and arrange them into directories such that fAB.txt becomes dA/fB.txt. As far as I can tell it should work however I get the below error running the code.
Moving!
mv: cannot move './f48.txt' to './d4/f8.txt': No such file or directory

Try with this.
removed unnecessary ";"
#! /bin/bash
for i in {0..9}
do
mkdir -p "d$i"
for j in {0..9}
do
if [ -e "./f$i$j.txt" ]
then
echo 'Moving!'
mv "./f$i$j.txt" "./d$i/f$j.txt"
fi
done
done

Related

move or copy a file if that file exists?

I am trying to run a command
mv /var/www/my_folder/reports.html /tmp/
it is running properly. But I want to put a condition like if that file exists then only run the command. Is there anything like that?
I can put a shell file instead.
for shell a tried below thing
if [ -e /var/www/my_folder/reports.html ]
then
mv /var/www/my_folder/reports.html /tmp/
fi
But I need a command. Can some one help me with this?
Moving the file /var/www/my_folder/reports.html only if it exists and regular file:
[ -f "/var/www/my_folder/reports.html" ] && mv "/var/www/my_folder/reports.html" /tmp/
-f - returns true value if file exists and regular file
if exist file and then move or echo messages through standard error output
test -e /var/www/my_folder/reports.html && mv /var/www/my_folder/reports.html /tmp/ || echo "not existing the file" >&2
You can do it simply in a shell script
#!/bin/bash
# Check for the file
ls /var/www/my_folder/ | grep reports.html > /dev/null
# check output of the previous command
if [ $? -eq 0 ]
then
# echo -e "Found file"
mv /var/www/my_folder/reports.html /tmp/
else
# echo -e "File is not in there"
fi
Hope it helps
Maybe your use case is "Create if not exist, then copy always".
then: touch myfile && cp myfile mydest/

linux zip and exclude dir via bash/shell script

I am trying to write a bash/shell script to zip up a specific folder and ignore certain sub-dirs in that folder.
This is the folder I am trying to zip "sync_test5":
My bash script generates an ignore list (based on) and calls the zip function like this:
#!/bin/bash
SYNC_WEB_ROOT_BASE_DIR="/home/www-data/public_html"
SYNC_WEB_ROOT_BACKUP_DIR="sync_test5"
SYNC_WEB_ROOT_IGNORE_DIR="dir_to_ignore dir2_to_ignore"
ignorelist=""
if [ "$SYNC_WEB_ROOT_IGNORE_DIR" != "" ];
then
for ignoredir in $SYNC_WEB_ROOT_IGNORE_DIR
do
ignorelist="$ignorelist $SYNC_WEB_ROOT_BACKUP_DIR/$ignoredir/**\*"
done
fi
FILE="$SYNC_BACKUP_DIR/$DATETIMENOW.website.zip"
cd $SYNC_WEB_ROOT_BASE_DIR;
zip -r $FILE $SYNC_WEB_ROOT_BACKUP_DIR -x $ignorelist >/dev/null
echo "Done"
Now this script runs without error, however it is not ignoring/excluding the dirs I've specified.
So, I had the shell script output the command it tried to run, which was:
zip -r 12-08-2014_072810.website.zip sync_test5 -x sync_test5/dir_to_ignore/**\* sync_test5/dir2_to_ignore/**\*
Now If I run the above command directly in putty like this, it works:
So, why doesn't my shell script exclude working as intended? the command that is being executed is identical (in shell and putty directly).
Because backslash quotings in a variable after word splitting are not evaluated.
If you have a='123\4', echo $a would give
123\4
But if you do it directly like echo 123\4, you'd get
1234
Clearly the arguments you pass with the variable and without the variables are different.
You probably just meant to not quote your argument with backslash:
ignorelist="$ignorelist $SYNC_WEB_ROOT_BACKUP_DIR/$ignoredir/***"
Btw, what actual works is a non-evaluated glob pattern:
zip -r 12-08-2014_072810.website.zip sync_test5 -x 'sync_test5/dir_to_ignore/***' 'sync_test5/dir2_to_ignore/***'
You can verify this with
echo zip -r 12-08-2014_072810.website.zip sync_test5 -x sync_test5/dir_to_ignore/**\* sync_test5/dir2_to_ignore/**\*
And this is my suggestion:
#!/bin/bash
SYNC_WEB_ROOT_BASE_DIR="/home/www-data/public_html"
SYNC_WEB_ROOT_BACKUP_DIR="sync_test5"
SYNC_WEB_ROOT_IGNORE_DIR=("dir_to_ignore" "dir2_to_ignore")
IGNORE_LIST=()
if [[ -n $SYNC_WEB_ROOT_IGNORE_DIR ]]; then
for IGNORE_DIR in "${SYNC_WEB_ROOT_IGNORE_DIR[#]}"; do
IGNORE_LIST+=("$SYNC_WEB_ROOT_BACKUP_DIR/$IGNORE_DIR/***") ## "$SYNC_WEB_ROOT_BACKUP_DIR/$IGNORE_DIR/*" perhaps is enough?
done
fi
FILE="$SYNC_BACKUP_DIR/$DATETIMENOW.website.zip" ## Where is $SYNC_BACKUP_DIR set?
cd "$SYNC_WEB_ROOT_BASE_DIR";
zip -r "$FILE" "$SYNC_WEB_ROOT_BACKUP_DIR" -x "${IGNORE_LIST[#]}" >/dev/null
echo "Done"
This is what I ended up with:
#!/bin/bash
# This script zips a directory, excluding specified files, types and subdirectories.
# while zipping the directory it excludes hidden directories and certain file types
[[ "`/usr/bin/tty`" == "not a tty" ]] && . ~/.bash_profile
DIRECTORY=$(cd `dirname $0` && pwd)
if [[ -z $1 ]]; then
echo "Usage: managed_directory_compressor /your-directory/ zip-file-name"
else
DIRECTORY_TO_COMPRESS=${1%/}
ZIPPED_FILE="$2.zip"
COMPRESS_IGNORE_FILE=("\.git" "*.zip" "*.csv" "*.json" "gulpfile.js" "*.rb" "*.bak" "*.swp" "*.back" "*.merge" "*.txt" "*.sh" "bower_components" "node_modules")
COMPRESS_IGNORE_DIR=("bower_components" "node_modules")
IGNORE_LIST=("*/\.*" "\.* "\/\.*"")
if [[ -n $COMPRESS_IGNORE_FILE ]]; then
for IGNORE_FILES in "${COMPRESS_IGNORE_FILE[#]}"; do
IGNORE_LIST+=("$DIRECTORY_TO_COMPRESS/$IGNORE_FILES/*")
done
for IGNORE_DIR in "${COMPRESS_IGNORE_DIR[#]}"; do
IGNORE_LIST+=("$DIRECTORY_TO_COMPRESS/$IGNORE_DIR/")
done
fi
zip -r "$ZIPPED_FILE" "$DIRECTORY_TO_COMPRESS" -x "${IGNORE_LIST[#]}" # >/dev/null
# echo zip -r "$ZIPPED_FILE" "$DIRECTORY_TO_COMPRESS" -x "${IGNORE_LIST[#]}" # >/dev/null
echo $DIRECTORY_TO_COMPRESS "compressed as" $ZIPPED_FILE.
fi
After a few trial and error, I have managed to fix this problem by changing this line:
ignorelist="$ignorelist $SYNC_WEB_ROOT_BACKUP_DIR/$ignoredir/**\*"
to:
ignorelist="$ignorelist $SYNC_WEB_ROOT_BACKUP_DIR/$ignoredir/***"
Not sure why this worked, but it does :)

Md5 Hash to identify and archive images

This is my first ever bash script and I am trying to iron out all of the creases and make the script run nicely. The script is to archive all of the specified .jpg files that it finds in multiple directories on a HDD/Flash drive. There are files with the same name but different content so I have used an Md5 sum to hash them.
I am getting the directory does not exist error in Geany but it runs fine from command bar missing out two of the images. I have tried everything I can think of to fix it. Is it messy code that is doing this?
#!/bin/sh
if [ ! -d "$1" ]; then
echo Directory "$1" cannot be found. Please try again.
exit
fi
if [ $# -eq 1 ]; then
echo "usage: Phar image_path archive_path"
exit
fi
if [ -d "$2" ]; then
echo "archive exists"
else
echo "the directory 'archive' does't exist. Creating directory 'archive'."
mkdir -p ~/archive
fi
find $1 -iname "IMG_[0-9][0-9][0-9][0-9].JPG" | cat > list.txt
[ -f ~/my-documents/md5.txt ] && rm md5.txt || break
while read line;
do md5sum $line | xargs >> md5.txt
done < list.txt
sort -k 1,1 -u md5.txt | cat > uniquemd5.txt
cut -d " " -f 2- uniquemd5.txt > uniquelist.txt
sort uniquelist.txt -r -o uniquelist.txt
for line in $(cat uniquelist.txt)
do
file=$(basename $line) path="$2/file"
if [ ! -f $path ];
then
cp $line $2
else
cp $line $path.JPG
fi
done
You haven't guarded against spaces in the folder and file names everywhere.
For instance:
cp $line $2
should be:
cp "$line" "$2"
You should start by eliminating these spaces as a source to your error by evaluating each variable you are referencing and adding ""'s.
If you still get the error please provide us with the arguments used and which directory that does not exist.

Bash is symlinking to working directory instead of specified directory

I am working on a bash script that I am working on for a universal Linux dotfile install script. I am attempting to get the symlinking working but I have been bashing (no pun intended) my head against the wall trying to figure out why the symlinks will not work and the copying will not work. I currently have this separated into multiple files so I don't have if statements three miles long.
ultimate-install.sh
#! /bin/bash
#
# The ultimate install script for all dotfiles.
if [[ -z "$1" ]]; then
echo "Please specify the directory where all of you dotfiles are located."
exit 1
fi
# Makes sure that the directory does NOT have a trailing slash!
if [[ ${1:(-1)} == "/" ]]; then
DOTFILE_DIR=${1:0:${#1} - 1}
else
DOTFILE_DIR="$1"
fi
# TODO: Clean this mess up and make it more concise.
if [[ -z "$2" ]]; then
if [[ ! -d $HOME/.config/old_dotfiles ]]; then
mkdir "$HOME/.config/old_dotfiles"
fi
BACKUP_DIR="$HOME/.config/old_dotfiles"
else
if [[ -d "$2" ]]; then
BACKUP_DIR="$2"
else
mkdir "$2"
BACKUP_DIR="$2"
fi
fi
read DECISION
if [ $DECISION == "N" -o $DECISION == "n" ]; then
echo "Aborting installation!"
exit
fi
read DECISION
echo
if [ $DECISION == "N" -o $DECISION == "n" ]; then
source src/no-prompts.sh "$DOTFILE_DIR" "$BACKUP_DIR"
else
source src/prompts.sh "$DOTFILE_DIR" "$BACKUP_DIR"
fi
echo "Installation complete. Old dotfiles are backed up to $BACKUP_DIR."
src/no-prompts.sh
#! /bin/bash
#
# Maintained by Daniel Seymour
DOTFILE_DIR="$1"
BACKUP_DIR="$2"
TEST_DIR="/home/daniel/dotfile-test"
function no_prompt_install(){
FILE_NAME="$1"
if [ "${FILE_NAME:0:1}" == "." ]; then
ln -s "$FILE_NAME $TEST_DIR/$FILE_NAME"
else
ln -s ".$FILE_NAME $TEST_DIR/$FILE_NAME"
fi
}
# TODO: implement a check for file type and deal with unknown files.
for FILE in $DOTFILE_DIR/*; do
cp $FILE $BACKUP_DIR
no_prompt_install $FILE
done
src/prompts.sh
#! /bin/bash
#
# Maintained by Daniel Seymour
DOTFILE_DIR="$1"
BACKUP_DIR="$2"
TEST_DIR="/home/daniel/dotfile-test"
function prompt_install {
FILE_PATH=$1
FILE_NAME=${FILE_PATH##*/}
echo "Would you like to install $FILE_NAME? [Y, n]"
read DECISION
if [ $DECISION == "n" -o $DECISION == "N" ]; then
echo "Not installing."
return
else
# TODO: Clean this up into one statement.
if [ ${FILE_NAME:0:1} == "." ]; then
rm -rf "$TEST_DIR/$FILE_NAME"
ln -sn "$FILE_PATH $TEST_DIR/$FILE_NAME"
else
FILE_NAME="."$FILE_NAME
rm -rf "$TEST_DIR/$FILE_NAME"
ln -sn "$FILE_PATH $TEST_DIR/$FILE_NAME"
fi
fi
}
# TODO: implement a check for file type and deal with unknown files.
for FILE in $DOTFILE_DIR/*; do
cp $FILE $BACKUP_DIR
prompt_install $FILE
done
The above is trimmed for long echo statements that do a lot of explaining.
The basic idea of this script is to take as many as two arguments (the dotfile directory to install and if specified, the custom backup directory, $1 and $2 respectively). The script should then copy all of the files in the target directory to BACKUP_DIR and symlink all of the dotfiles in the DOTFILE_DIR to TEST_DIR. (TEST_DIR will be $HOME in the production scripts.) Great in theory, right?
The complication comes when I run the script. None of the files are copied or symlinked as they should be. Instead, I end up with NO copy (probably due to the same issue as the symlink not working) and a broken symlink in the current directory.
One last piece of information. I am executing the file from the directory that contains ultimate-install.sh (/home/daniel/Projects/Git-Repos/Ultimate-Dotfile-Install-Scripts).
So where did I go wrong?
PS Please don't comment on the TODOs. :)
Short answer
Your quoting is wrong.
ln -sn -- "$FILE_PATH" "$TEST_DIR/$FILE_NAME"
Longer answer
This does not really relate to your problem, but I want to point it out.
Do not use "" inside [[ ]], so instead of this if [[ -z "$1" ]]; then use this if [[ -z $1 ]]; then
What is the point of making sure that directory does not have a trailing slash? It has no effect! /usr/bin/ is the same directory as /usr/bin or /usr////bin or /usr////////bin//////
Do not check if a directory exists when creating directories. Use -p option! Example: mkdir -p "$HOME/.config/old_dotfiles"
Instead of if [ $DECISION == "N" -o $DECISION == "n" ]; use if [[ ${DECISION^^} == N]];
I have another great answer about bash code style HERE. Please check it out! Also read the comments, since I was explaining there exactly your issue.

restore in linux bash scripting

Help needed. This is script that I use to perform a restoration of a file from dustbin directory to its original location. It was located before in root. Then using other script it was "deleted" and stored in dustbin directory, and its former location was documented in storage file using this:
case $ans in
y) echo "`readlink -f $1`" >>home/storage & mv $1 /home/dustbin ;;
n) echo "File not deleted." ;;
*) echo "Please input answer." ;;
esac
So when using the script below I should restore the deleted file, but the following error comes up.
#!/bin/sh
if [ "$1" == "-n" ] ; then
cd ~/home/dustbin
restore="$(grep "$2" "$home/storage")"
filename="$(basename "$restore")"
echo "Where to save?"
read location
location1="$(readlink -f "$location")"
mv -i $filename "$location1"/$filename
else
cd ~/home
storage=$home/storage
restore="$(grep "$1" "$storage")"
filename="$(basename "$restore")"
mv -i $filename $restore
fi
error given - mv: missing file operand
EDIT:
so okay, I changed my script to something like this.
#!/bin/sh
if [ $1 ] ; then
cd ~/home
storage=~/home/storage
restore="$(grep "$1" "$storage")"
filename="$(basename "$restore")"
mv -i "$filename" "$restore"
fi
and still I get error:
mv: cannot stat `filename': No such file or directory
You might want to do some basic error handling to see if $filename exists before you use it as part of mv:
For example, before:
mv -i $filename "$location1"/$filename
You should probably do a:
if [[ -e "$filename" ]]; then
# do some error handling if you haven't found a filename
fi
The -e option checks whether the next argument to [[ refers to a filename that exists. It evaluates to true if so, false otherwise. (Alternatively, use -f to check if it's a regular file)
Or at least:
if [[ -z "$filename" ]]; then
# do some error handling if you haven't found a filename
fi
The -z option checks whether the next argument to [[ is the empty string. It evaluates to true if so, false otherwise.
Similar comment about: mv -i $filename $restore in your else clause.
Here's a list of test options.
You do
cd ~/home
and
mv -i "$filename" "$restore"
while the file is located in the dustbin directory, therefore, it is not found.
Do either
cd ~/home/dustbin
or
mv -i "dustbin/$filename" "$restore"
or just do
mv -i "~/home/dustbin/$filename" "$restore"
and drop the cd.

Resources