How to set a Directory as an Argument in Bash - bash

I am having trouble finding out how to set a directory as an argument in bash.
The directory I am trying to have as an argument is /home/rrodriguez/Documents/one.
Anywhere I try to look for an answer I see examples like dir = $1 but I cant seem to find an explanation of what this means or how to set it up so that it references my specific file location. Could anyone show me how to set up my variable for my path directory?
Adding my code for a better understanding of what im trying to do:
#!bin/bash
$1 == 'home/rrodriguez/Documents/one/'
dir = $1
touch -c $dir/*
ls -la $dir
wc$dir/*

Consider:
#!bin/bash
dir=$1
touch -c "$dir"/*
ls -la "$dir"
This script takes one argument, a directory name, and touches files in that directory and then displays a directory listing. You can run it via:
bash script.sh 'home/rrodriguez/Documents/one/'
Since home/rrodriguez/Documents/one/ is the first argument to the script, it is assigned to $1 in the script.
Notes
In shell, never put spaces on either side of the = in an assignment.
I omitted the line wc$dir/* because it wasn't clear to me what the purpose of it was.
I put double-quotes around $dir to prevent the shell from, among other things, performing word-splitting. This would matter if dir contains spaces.

Related

mv: cannot stat [DIRECTORY/FILE]: no such file or directory

EDIT: DIR_trash="trash"
I wrote a function to move a file to current directory.
if [ "$1" == "-u" ]
then
if [ $# == 1 ]
then
echo "Something went wrong. Please make sure you're passing the name of the file/directory after '-u'."
else
if [ -f $DIR_trash/$2.zip ]
then
echo "$2.zip has been found in the trash."
cd
cd $DIR_trash
sed -i "/$2/d" $file7
mv -i /$DIR_trash/$2.zip .
unzip $2.zip
\rm $2.zip
cd
else
echo "$2.zip has not been found in the trash."
fi
fi
fi
As you can see, there is a line of code which says:
mv -i /DIR_trash/$2.zip .
So basically I'm trying to move a file that I passed in argument 2 to current directory, from trash. I always run this script from home directory, which does have trash directory. This is what I get when I run this:
Whenever I manually write this is in the Konsole (from home direcotry) it does work:
rm -u trash/d1 .
I'm out of ideas. Could anyone please help?
Let's say you run the script with the current directory being /some/where, and with the arguments -u and d1. I'll also assume that your home directory is /home/ninini. Let's look at where your script looks for files.
DIR_trash="trash"
if [ -f $DIR_trash/$2.zip ]
You check if /some/where/trash/d1.zip exists.
cd
cd $DIR_trash
Assuming both cd commands succeed, the current directory is now /home/ninini/trash.
mv -i /$DIR_trash/$2.zip .
You're saying to move /trash/d1.zip to the current directory, which is /home/ninini/trash.
Neither the source nor the destination make sense. The source /$DIR_trash doesn't make sense: why would you be looking for a directory called trash under the root directory? And the destination doesn't make sense since you just attempted to change to the trash directory, and now you're attempting to move a file out of the trash directory… into the trash directory.
I can't tell what the correct code is because you didn't say what the script is meant to do. You do say that you want to “to move a file to current directory”; then you must not change the current directory midway through the script! Assuming that the path $DIR_trash/$2.zip from the test command is the correct one, remove the cd commands and write
mv -i -- "$DIR_trash/$2.zip" .
Note that this moves the file from a directory called trash under the current directory. If this isn't what you wanted, you need to change the definition of DIR_trash. It should probably be an absolute path, perhaps
DIR_trash=~/trash
Note also that your script breaks on files containing whitespace and other special characters. Always put double quotes around variable substitutions: "$VAR", not $VAR. (Exception: when you know you need some effect that the double quotes prevent, and you understand why it's safe to leave them out.)

Questions about bash

Firstly, I'm wondering how to input information from the terminal into a variable in the script file. For example, lets say I wanted to do ./name.sh dave in the terminal instead of using read -p to ask for the name in the script. Secondly, I'm wondering how to go about creating a new directory and then copying files into that directory. I know how to use the mkdir command, but not how to copy files to that new directory.
Sorry if my wording is a bit bad I wasn't sure how else to ask the questions (this is my first day messing with bash.)
When you run:
./name.sh dave
the string dave will be the first positional argument in the script. You can access it with $1. To create a directory named dave and copy files into it, you might do:
#!/bin/bash
dir=${1:?}
mkdir "$dir" || exit
cp * "$dir"
A few things are a bit cryptic, and perhaps you might prefer:
#!/bin/sh
if test -z "$1"; then
echo "Parameter missing" >&2;
exit 1
fi
mkdir "$1" && cp * "$1"
Basically, you access the parameters via $1, $2, etc. The ${1:?} syntax is a shortcut that assigns the variable dir, but aborts the script if $1 is unset or empty. (eg, if you call the script without an argument.)
The rest seems pretty self-explanatory.
Suppose you wanted to specify the files to copy, so that ./name.sh dave would create a directory named dave and copy all files in the current directory to it (as above), but if you pass more arguments it would copy only those files. In that case, you might do something like:
#!/bin/bash
dir=${1:?}
shift # Discard the first argument, shift remaining down
mkdir "$dir" || exit
case $# in
0) cp * "$dir";;
*) cp "$#" "$dir";;
esac
Here, "$#" is the list of each argument, individually quoted. (eg, if you call the script with an argument that has spaces, it will properly pass that argument to cp. Compare that with cp $# $dir or cp "$*" $dir.) If you're just starting with shell scripts, I would advise you always be careful about quotes.

In bash, what's the best way for a script to reference the path of another script?

scripts/a.sh calls scripts/b.sh through source or through sh.
But I cannot be sure that the working directory will be scripts or the parent of scripts or something else.
What is the best practice for referencing b.sh? I can find the directory of the current script, then cd to that directory, and then simply call ./b.sh. But that seems like a lot of code to put into every script that calls another.
There is no need for a cd, cause source or command take a full path. Just get the dir name of the full path of your script and run the script from there.
From bash manual:
0
($0) Expands to the name of the shell or shell script. ....
From man readlink:
-f, --canonicalize
canonicalize by following every symlink in every component of the given name recursively; ...
From man dirname:
dirname - strip non-directory suffix from file name
Altogether:
. "$(dirname "$(readlink -f "$0")")"/b.sh
I've seen some bash scripts that start with something similar to:
DIR=$(dirname "$(readlink -f "$0")")
cd "$DIR"
So the current working directory in a script stays the same, even if user runs it from another directory.
#edit
Like #GordonDavisson suggested in comments, we can also add your dir to PATH:
export PATH="$(dirname "$(readlink -f "$0")")":"$PATH"
Then running:
. a.sh
will search for a.sh script through inside directories listed in PATH variable, which it will find in the first dir.

In shell script, how to change current directory safely with variable?

The following shell script changes current the directory to the desktop.
v=~/Desktop/
cd $v
pwd # desktop
The following script changes the current directory to home directory instead of generating error.
cd $undefined_variable
pwd # home directory
echo $? # 0
I'm afraid that the script will remove important files if I misspelled a variable for new current directory.
Generally, how do you safely change current directory with variable in shell script?
Use:
cd ${variable:?}
if $variable is not defined or empty then bash will throw an error and exit. It's like the set -u option but not global through the file.
You can set -u to make bash exit with an error each time you expand an undefined variable.
You could use the test -d condition (checks whether the specified variable is a directory), i.e.
if [[ -d $undefined_variable ]]
then
cd $undefined_variable
echo "This will not be printed if $undefined_variable is not defined"
fi
See also here for further test options...
The Bourne Shells have a construct to substitute a value for undefined variables, ${varname-subtitution}. You can use this to have a safe fallback directory in case the variable is undefined:
cd "${undefined-/tmp/backupdir}"
If there is a variable named undefined, its value is substituted, otherwise /tmp/backupdir is substituted.
Note that I also put the variable expansion in double quotes. This is used to prevent word splitting on strings containing spaces (very common for Windows directories). This way it works even for directories with spaces.
For the gory details on all the shell substitution constructs (there are seven more for POSIX shells), read your shell manual's Parameter Substitution section.
You have to write a wrapper (this work in bash):
cd() {
if [ $# -ne 1 ] ;then
echo "cd need exactly 1 argument" >&2
return 2
fi
builtin cd "$1"
}
yes, that's shell
if you type cd without parameter it will jump to home dir.
You can can check the variable of null or empty before you cd command.
check like (cd only be called if targetDir is not empty):
test -z "$targetDir" || cd $targetDir
check like (cd only be called if targetDir really exist):
test -d "$targetDir" && cd $targetDir
Note: Thanks for -1, should read the last sentence too. So I added the real answer.

Getting relative paths in BASH

I already searched for this, but I guess there was no great demand on working with paths.
So I'm trying two write a bash script to convert my music collection using tta and cue files.
My directory structure is as following: /Volumes/External/Music/Just/Some/Dirs/Album.tta for the tta files and /Volumes/External/Cuesheets/Just/Some/Dirs/Album.cue for cue sheets.
My current approach is setting /Volumes/External as "root_dir" and get the relative path of the album.tta file to $ROOT_DIR/Music (in this case this would be Just/Some/Dirs/Album.tta), then add this result to $ROOT_DIR/Cuesheets and change the suffix from .tta to .cue.
My current problem is, that dirname returns paths as they are, which means /Volumes/External/Music/Just/Some/Dirs does not get converted to ./Just/Some/Dirs/ when my current folder is $ROOT_DIR/Music and the absolute path was given.
Add://Here is the script if anybody has similar problems:
#!/bin/bash
ROOT_DIR=/Volumes/External
BASE="$1"
if [ ! -f "$BASE" ]
then
echo "Not a file"
exit 1
fi
if [ -n "$2" ]
then
OUTPUT_DIR="$HOME/tmp"
else
OUTPUT_DIR="$2"
fi
mkfdir -p "$OUTPUT_DIR" || exit 1
BASE=${BASE#"$ROOT_DIR/Music/"}
BASE=${BASE%.*}
TTA_FILE="$ROOT_DIR/Music/$BASE.tta"
CUE_FILE="$ROOT_DIR/Cuesheets/$BASE.cue"
shntool split -f "${CUE_FILE}" -o aiff -t "%n %t" -d "${OUTPUT_DIR}" "${TTA_FILE}"
exit 0
If your Cuesheets dir is always in the same directory as your Music, you can just remove root_dir from the path, and what is left is the relative path. If you have the path to your album.tta in album_path (album_path=/Volumes/External/Music/Just/Some/Dirs/Album.tta) and your root_dir set(root_dir=/Volumes/External), just do ${album_path#$root_dir}. This trims root_dir from the front of album_path, so you are left with album_path=Just/Some/Dirs/Album.tta.
See bash docs for more information on bash string manipulation
EDIT:// Changed ${$album_path#$root_dir} to ${album_path#$root_dir}
Okay so I've tackled this a couple of ways in the past. I don't recommend screwing with paths and pwd environment variables, I've seen some catastrophic events because of it.
Here's what I would do
CURRENTDIR=/Volumes/External/Music # make sure you check the existence in your script
...
SEDVAL=$(echo $CURRENTDIR | sed s/'\/'/'\\\/'/g)
#run your loops for iterating through files
for a in $(find ./ -name \*ogg); do
FILE=`echo $a | sed s/$SEDVAL/./g` # strip the initial directory and replace it with .
convert_file $FILE # whatever action to be performed
done
If this is something you might do frequently I would actually just write a separate script just for this.

Resources