When I try to execute my script qwer.bash like this:
bash qwer.bash *whatever #whatever can really be anything
it will give back to me:
qwer.bash: line 5: [[: *whatever: syntax error: operand expected (error token is "*whatever")
here is my script:
#!/bin/bash
declare -a files
while [[ "$1" -ne "-p" ]]
do
echo "pwet"
shift
done
How can I avoid producing this error?
This is because the test $1 -ne ... generates an error when$1 is not an integer.
Try this:
while [[ "${#}" > 0 && "${1}" != "-p" ]]; do
echo "pwet"
shift
done
When shift is executed, ${#} decrements.
When "${#}" > 0 is false, the while loop ends. Note that && instructs the shell to not evaluate the second operand ("${1}" != "-p") when the first one is already false.
Though this is irrelevant in your case
Regarding
bash qwer.bash *whatever #whatever can really be anything
Case 1: If *whatever is meant to glob files :
bash qwer.bash ./*whatever
or
bash qwer.bash -- *whatever # here -- marks the end of options
or
./qwer.bash -- *whatever # you already have a shebang in your script.
This is to handle a case where you actually have a file named -file-with-dash in the current folder which may wrongly be taken as an option
Case 2: If *whatever is just a string:
bash qwer.bash \*whatever
or
bash qwer.bash "*whatever"
Related
I have this piece of code in Bash,
The goal is to compare two files and if files matches and also no force argument is passed, then exit.
Other way it should continue...
But I got this error [: !=: unexpected operator when I run with no argument and script is not stopped as expected,
When I pass force argument it works OK.
Any Idea please ?
if cmp -s file1 file2 && [ $1 != "-f" ] ; then
"do something"
exit 1
else "do something"
fi
[ ... ] is just a normal command, essentially equivalent to test. As such, if you pass an unquoted empty variable (like $1 when no arguments are provided to your program), it will try to run [ != "-f" ], which gives that error, since you need two sides to compare with !=.
To solve this, you can either use [[ ... ]] (which requires bash, not being POSIX compatible), which does not make unquoted variables "disappear", or you can also quote your variable to keep it POSIX compatible:
cmp -s file1 file2 && [[ $1 != "-f" ]]
cmp -s file1 file2 && [ "$1" != "-f" ]
I wrote a script to fetch the Top Level Session PID, that is, the session starter, who may be a shell like bash, dash, ksh, or even systemd. The script may get a PID as a initial parameter however I need to filter it to check it is a valid integer and not something like 34fg45, -5467 and I don't want it starting with a zero like 05467.
This is a snippet of the script.
if [ "$1" != "" ]; then
if [[ "$1" == [1-9]*([0-9]) ]]; then <- Check for Integer; error here in non bash shell
if ps -p $1 -o "pid=" >/dev/null 2>&1; then
pid=$1
else
echo "PID $1, no such process." >&2
exit 1
fi
else
echo "Invalid pid." >&2
exit 1
fi
else
pid=$$
fi
The code runs in bash, but fails to run on dash with a syntax error:
./tspid: 16: ./tspid: Syntax error: "(" unexpected (expecting "then")
It is my understanding that
if [[ "$1" =~ ^[0-9][1-9]*$ ]]; using =~ does regular expression matching, and
if [[ "$1" == [1-9]*([0-9]) ]]; using == does pattern matching
Is that right?
How to transform the above expressions to run in both, non-bash as well in bash shells ?
Use case conditional construct. Every POSIX shell has it and unlike double brackets it doesn't look horrible.
# make sure 0-9 is literally 0 to 9
LC_COLLATE=C
# assume set -u is not in effect or $1 is set
case $1 in
('')
# handle empty argument
;;
(0*|*[!0-9]*)
# handle invalid PID (0, 042, 42a, etc.)
;;
(*)
# handle valid PID
;;
esac
# restore LC_COLLATE if necessary
i'm stucked on this problem: i need a dynamic expression generation that has to be passed to ls command.
Here the code i tried:
op="ext"
fileName="MDL_test_"
fileExt=".csv"
if [[ $op == "noext" ]] ; then
searchExp="*$fileName*"
else
searchExp="*$fileName*$fileExt"
fi
ls "$("./files/"$searchExp)"
But when i execute the script this is what i get:
./ext_test.sh: line 15: ./files/MDL_test_30160410.csv.gz: Permission denied
ls: cannot access : No such file or directory
I think i'm doing something wrong, but i can't figure out it...
You just need to build the string up in pieces; most of the syntax is evaluated by the shell before passing the expanded result to ls.
if [[ $op == noext ]]; then
fileExt=
else
fileExt=.csv
fi
ls ./files/*"$fileName"*"$fileExt"
I'd like to add an argument to a command in bash only if a variable evaluates to a certain value. For example this works:
test=1
if [ "${test}" == 1 ]; then
ls -la -R
else
ls -R
fi
The problem with this approach is that I have to duplicate ls -R both when test is 1 or if it's something else. I'd prefer if I could write this in one line instead such as this (pseudo code that doesn't work):
ls (if ${test} == 1 then -la) -R
I've tried the following but it doesn't work:
test=1
ls `if [ $test -eq 1 ]; then -la; fi` -R
This gives me the following error:
./test.sh: line 3: -la: command not found
A more idiomatic version of svlasov's answer:
ls $( (( test == 1 )) && printf %s '-la' ) -R
Since echo understands a few options itself, it's safer to use printf %s to make sure that the text to print is not mistaken for an option.
Note that the command substitution must not be quoted here - which is fine in the case at hand, but calls for a more robust approach in general - see below.
However, in general, the more robust approach is to build up arguments in an array and pass it as a whole:
# Build up array of arguments...
args=()
(( test == 1 )) && args+=( '-la' )
args+=( '-R' )
# ... and pass it to `ls`.
ls "${args[#]}"
Update: The OP asks how to conditionally add an additional, variable-based argument to yield ls -R -la "$PWD".
In that case, the array approach is a must: each argument must become its own array element, which is crucial for supporting arguments that may have embedded whitespace:
(( test == 1 )) && args+= ( '-la' "$PWD" ) # Add each argument as its own array element.
As for why your command,
ls `if [ $test -eq 1 ]; then -la; fi` -R
didn't work:
A command between backticks (or its modern, nestable equivalent, $(...)) - a so-called command substitution - is executed just like any other shell command (albeit in a sub-shell) and the whole construct is replaced with the command's stdout output.
Thus, your command tries to execute the string -la, which fails. To send it to stdout, as is needed here, you must use a command such as echo or printf.
Print the argument with echo:
test=1
ls `if [ $test -eq 1 ]; then echo "-la"; fi` -R
I can't say how acceptable this is, but:
test=1
ls ${test:+'-la'} -R
See https://stackoverflow.com/revisions/16753536/1 for a conditional truth table.
Another answer without using eval and using BASH arrays:
myls() { local arr=(ls); [[ $1 -eq 1 ]] && arr+=(-la); arr+=(-R); "${arr[#]}"; }
Use it as:
myls
myls "$test"
This script builds whole command in an array arr and preserves the original order of command options.
I wrote a bash script that uploads a file on my home server. It gets activated from a folder action script using applescript. The setup is the folder on my desktop is called place_on_server. Its supposed to have an internal file structure exactly like the folder I want to write to: /var/www/media/
usage goes something like this:
if directory etc added to place_on_server: ./upload DIR etc
if directory of directory: etc/movies ./upload DIR etc movies //and so on
if file to place_on_server: ./upload F file.txt
if file in file in place_on_server ./upload F etc file.txt //and so on
for creating a directory its supposed to execute a command like:
ssh root#192.168.1.1<<EOF
cd /var/www/media/wherever
mkdir newdirectory
EOF
and for file placement:
rsync -rsh='ssh -p22' file root#192.168.1.1:/var/www/media/wherever
script:
#!/bin/bash
addr=$(ifconfig -a | ./test)
if ($# -le "1")
then
exit
elif ($1 -eq "DIR")
then
f1="ssh -b root#$addr<<EOF"
list = "cd /var/www/media\n"
if($# -eq "2")
then
list=list+"mkdir $2\nEOF\n"
else
num=2
i=$(($num))
while($num < $#)
do
i=$(($num))
list=list+"mkdir $i\n"
list=list+"cd $i\n"
$num=$num+1
done
fi
echo $list
elif ($1 -eq "F")
then
#list = "cd /var/www/media\n"
f2="rsync -rsh=\'ssh -p22\' "
f3 = "root#$addr:/var/www/media"
if($# -eq "2")
then
f2=f2+$2+" "+f3
else
num=3
i=$(($num))
while($num < $#)
do
i=$(($num))
f2=f2+"/"+$i
$num=$num+1
done
i=$(($num))
f2=f2+$i+" "+$f3
fi
echo $f2
fi
exit
output:
(prompt)$ ./upload2 F SO test.txt
./upload2: line 3: 3: command not found
./upload2: line 6: F: command not found
./upload2: line 25: F: command not found
So as you can see I'm having issues handling input. Its been awhile since I've done bash. And it was never extensive to begin with. Looking for a solution to my problem but also suggestions. Thanks in advance.
For comparisons, use [[ .. ]]. ( .. ) is for running commands in subshells
Don't use -eq for string comparisons, use =.
Don't use < for numerical comparisons, use -lt
To append values, f2="$f2$i $f3"
To add line feeds, use $'\n' outside of double quotes, or a literal linefeed inside of them.
You always need "$" on variables in strings to reference them, otherwise you get the literal string.
You can't use spaces around the = in assignments
You can't use $ before the variable name in assignments
To do arithmetics, use $((..)): result=$((var1+var2))
For indirect reference, such as getting $4 for n=4, use ${!n}
To prevent word splitting removing your line feeds, double quote variables such as in echo "$line"
Consider writing smaller programs and checking that they work before building out.
Here is how I would have written your script (slightly lacking in parameter checking):
#!/bin/bash
addr=$(ifconfig -a | ./test)
if [[ $1 = "DIR" ]]
then
shift
( IFS=/; echo ssh "root#$addr" mkdir -p "/var/www/media/$*"; )
elif [[ $1 = "F" ]]
then
shift
last=$#
file=${!last}
( IFS=/; echo rsync "$file" "root#$addr:/var/www/media/$*" )
else
echo "Unknown command '$1'"
fi
$* gives you all parameters separated by the first character in $IFS, and I used that to build the paths. Here's the output:
$ ./scriptname DIR a b c d
ssh root#somehost mkdir -p /var/www/media/a/b/c/d
$ ./scriptname F a b c d somefile.txt
rsync somefile.txt root#somehost:/var/www/media/a/b/c/d/somefile.txt
Remove the echos to actually execute.
The main problem with your script are the conditional statements, such as
if ($# -le "1")
Despite what this would do in other languages, in Bash this is essentially saying, execute the command line $# -le "1" in a subshell, and use its exit status as condition.
in your case, that expands to 3 -le "1", but the command 3 does not exist, which causes the error message
./upload2: line 3: 3: command not found
The closest valid syntax would be
if [ $# -le 1 ]
That is the main problem, there are other problems detailed and addressed in that other guy's post.
One last thing, when you're assigning value to a variable, e.g.
f3 = "root#$addr:/var/www/media"
don't leave space around the =. The statement above would be interpreted as "run command f3 with = and "root#$addr:/var/www/media" as arguments".