Shellscript backslashed and spaced parameter - bash

So, what I'm trying to do is if the user didn't pass a path as argument to script, the script shall use the current directory. If a path is passed use it instead.
instdir="$(pwd)/"
if [ -n "$1" ] ; then
instdir="$1"
fi
cd $instdir
Errors
./script.sh /path/to/a\ folder/
outputs: cd: /path/to/a: File or folder not found
./script.sh "/path/to/a\ folder/"
outputs: cd: /path/to/a\: File or folder not found
What am I doing wrong here?

Changing cd $instdir to cd "$instdir" should fix that particular problem. Without the quotes, the a and folder parts of a folder are treated as separate parameters.
Note, instead of the three-line if statement to set instdir, write:
[ "$1" ] && instdir="$1"

If you pass a path with spaces in it as an argument, it will cause problems. If not now, then in the future. I'd suggest you do the following (provided the path is the only argument):
instdir="$(pwd)"
if [[ -d "$#" ]]; then
instdir="$#"
fi
cd "$instdir"

Related

Prevent directory traversal vulnerability in bash script

How can I prevent directory traversal attacks in a bash script, where arguments contain directory names?
Example:
$STAGE=$1
$APP=$2
deploy.sh dist/ /opt/apps/"$STAGE"/"$APP"
The $STAGE and $APP variables are set from outside. An attacker could change this to an arbitrary path with "..".
I know the usual solution is to compare the directory string with the result of a function that returns the absolute path. But I couldn't find a ready solution and don't want to come up with my own.
Something like this?
#! /bin/bash
STAGE=$1
APP=$2
expectedParentDir="/opt/apps/"
function testDir(){
arg=$1
if [[ ! -f $arg ]]
then
echo "File $arg does not exist."
exit 1
fi
rpath=$(realpath $arg)
if [[ $rpath != ${expectedParentDir}* ]]
then
echo "Please only reference files under $expectedParentDir directory."
exit 2
fi
}
testDir /opt/apps/"$STAGE"/"$APP"
... deploy ...
Example Call
test.sh "../../etc/" "passwd"
Please only reference files under /opt/apps/ directory.
------------
test.sh "../../etc/" "secret"
File /opt/apps/../../etc//secret does not exist.
Test existence of file with -f or use -d if target must be a directory
Use realpath to resolve path
Use == ${expectedParentDir}* to find out if resolved path starts with expected string
The script should be run as a user that only has permissions to access the necessary directories.

Is it possible to CD into a file?

I find a list of files that I need to cd to (obviously to the parent directory).
If I do cd ./src/components/10-atoms/fieldset/package.json I get the error cd: not a directory:, which makes sense.
But isn't there a way to allow for that? Because manipulating the path-string is pretty cumbersome and to me that would make total sense to have an option for that, since cd is a directory function and it would be cool that if the path would not end up in a file, it would recursively jump higher and find the "first dir" from the given path.
So cd ./src/components/10-atoms/fieldset/package.json would put me into ./src/components/10-atoms/fieldset/ without going on my nerves, telling me that I have chosen a file rather than a dir.
You could write a shell function to do it.
cd() {
local args=() arg
for arg in "$#"; do
if [[ $arg != -* && -e $arg && ! -d $arg ]]; then
args+=("$(dirname "$arg")")
else
args+=("$arg")
fi
done
builtin cd ${args[0]+"${args[#]}"}
}
Put it in your ~/.bashrc if you want it to be the default behavior. It won't be inherited by shell scripts or other programs so they won't be affected.
It modifies cd's arguments, replacing any file names with the parent directory. Options with a leading dash are left alone. command cd calls the underlying cd builtin so we don't get trapped in a recursive loop.
(What is this unholy beast: ${args[0]+"${args[#]}"}? It's like "${args[#]}", which expands the array of arguments, but it avoids triggering a bash bug with empty arrays on the off chance that your bash version is 4.0-4.3 and you have set -u enabled.)
This function should do what you need:
cdd() { test -d "$1" && cd "$1" || cd $(dirname "$1") ; }
If its first argument "$1" is a directory, just cd into it,
otherwise cd into the directory containing it.
This function should be improved to take into account special files such as devices or symbolic links.
You can if you enter a bit longer line (or create dedicated shell script)
cd $(dirname ./src/components/10-atoms/fieldset/package.json)
If you add it in script it can be :
cd $(dirname $1)
but you need to execute it on this way:
. script_name ./src/components/10-atoms/fieldset/package.json
You can put this function in your ~/.bashrc:
function ccd() {
TP=$1 # destination you're trying to reach
while [ ! -d $TP ]; do # if $TP is not a directory:
TP=$(dirname $TP) # remove the last part from the path
done # you finally got a directory
cd $TP # and jump into it
}
Usage: ccd /etc/postfix/strangedir/anotherdir/file.txt will get you to /etc/postfix.

Bash script - File directory does not exist

I'm creating a very simple bash script that will check to see if the directory exists, and if it doesn't, create one.
However, no matter what directory I put in it doesn't find it!
Please tell me what I'm doing wrong.
Here is my script.
#!/bin/bash
$1="/media/student/System"
if [ ! -d $1 ]
then
mkdir $1
fi
Here is the command line error:
./test1.sh: line 2: =/media/student/System: No such file or directory
Try this
#!/bin/bash
directory="/media/student/System"
if [ ! -d "${directory}" ]
then
mkdir "${directory}"
fi
or even shorter with the parent argument of mkdir (manpage of mkdir)
#!/bin/bash
directory="/media/student/System"
mkdir -p "${directory}"
In bash you are not allow to start a variable with a number or a symbol except for an underscore _. In your code you used $1 , what you did there was trying to assign "/media/student/System" to $1, i think maybe you misunderstood how arguments in bash work. I think this is what you want
#!/bin/bash
directory="$1" # you have to quote to avoid white space splitting
if [[ ! -d "${directory}" ]];then
mkdir "$directory"
fi
run the script like this
$ chmod +x create_dir.sh
$ ./create_dir.sh "/media/student/System"
What the piece of code does is to check if the "/media/student/System" is a directory, if it is not a directory it creates the directory

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.

Quick bash script to run a script in a specified folder?

I am attempting to write a bash script that changes directory and then runs an existing script in the new working directory.
This is what I have so far:
#!/bin/bash
cd /path/to/a/folder
./scriptname
scriptname is an executable file that exists in /path/to/a/folder - and (needless to say), I do have permission to run that script.
However, when I run this mind numbingly simple script (above), I get the response:
scriptname: No such file or directory
What am I missing?! the commands work as expected when entered at the CLI, so I am at a loss to explain the error message. How do I fix this?
Looking at your script makes me think that the script you want to launch a script which is locate in the initial directory. Since you change you directory before executing it won't work.
I suggest the following modified script:
#!/bin/bash
SCRIPT_DIR=$PWD
cd /path/to/a/folder
$SCRIPT_DIR/scriptname
cd /path/to/a/folder
pwd
ls
./scriptname
which'll show you what it thinks it's doing.
I usually have something like this in my useful script directory:
#!/bin/bash
# Provide usage information if not arguments were supplied
if [[ "$#" -le 0 ]]; then
echo "Usage: $0 <executable> [<argument>...]" >&2
exit 1
fi
# Get the executable by removing the last slash and anything before it
X="${1##*/}"
# Get the directory by removing the executable name
D="${1%$X}"
# Check if the directory exists
if [[ -d "$D" ]]; then
# If it does, cd into it
cd "$D"
else
if [[ "$D" ]]; then
# Complain if a directory was specified, but does not exist
echo "Directory '$D' does not exist" >&2
exit 1
fi
fi
# Check if the executable is, well, executable
if [[ -x "$X" ]]; then
# Run the executable in its directory with the supplied arguments
exec ./"$X" "${#:2}"
else
# Complain if the executable is not a valid
echo "Executable '$X' does not exist in '$D'" >&2
exit 1
fi
Usage:
$ cdexec
Usage: /home/archon/bin/cdexec <executable> [<argument>...]
$ cdexec /bin/ls ls
ls
$ cdexec /bin/xxx/ls ls
Directory '/bin/xxx/' does not exist
$ cdexec /ls ls
Executable 'ls' does not exist in '/'
One source of such error messages under those conditions is a broken symlink.
However, you say the script works when run from the command line. I would also check to see whether the directory is a symlink that's doing something other than what you expect.
Does it work if you call it in your script with the full path instead of using cd?
#!/bin/bash
/path/to/a/folder/scriptname
What about when called that way from the command line?

Resources