I want to dynamically generate pretty long bash command depending on the command line options. Here is what I tried:
CONFIG_PATH=""
#Reading CONFIG_PATH from getopts if supplied
SOME_OPT=""
if [ ! -z "$CONFIG_PATH" ]; then
SOME_OPT="-v -s -cp $CONFIG_PATH"
fi
some_bash_command $SOME_OPT
The point here is that I want to pass 0 arguments to the some_bash_command if no arguments were passed to the script. In case there were some arguments I want to pass them.
It works fine, but the problem is that this approach looks rather unnatural to me.
What would be a better yet practical way to do this?
Your approach is more-or-less the standard one; the only significant improvement that I'd recommend is to use an array, so that you can properly quote the arguments. (Otherwise your command can horribly misbehave if any of the arguments happen to include special characters such as spaces or asterisks.)
So:
SOME_OPT=()
if [ ! -z "$CONFIG_PATH" ]; then
SOME_OPT=(-v -s -cp "$CONFIG_PATH")
fi
some_bash_command "${SOME_OPT[#]}"
Related
I was trying to write a Bash script that uses an if statement.
if[$CHOICE -eq 1];
The script was giving me errors until I gave a space before and after [ and before ] as shown below:
if [ $CHOICE -eq 1 ];
My question here is, why is the space around the square brackets so important in Bash?
Once you grasp that [ is a command, a whole lot becomes clearer!
[ is another way to spell "test".
help [
However while they do exactly the same, test turns out to have a more detailed help page. Check
help test
...for more information.
Furthermore note that I'm using, by intention, help test and not man test. That's because test and [ are shell builtin commands nowadays. Their feature set might differ from /bin/test and /bin/[ from coreutils which are the commands described in the man pages.
From another question:
A bit of history: this is because '[' was historically not a shell-built-in but a separate executable that received the expresson as arguments and returned a result. If you didn't surround the '[' with space, the shell would be searching $PATH for a different filename (and not find it) . – Andrew Medico Jun 24 '09 at 1:13
[ is a command and $CHOICE should be an argument, but by doing [$CHOICE (without any space between [ and $CHOICE) you are trying to run a command named [$CHOICE. The syntax for command is:
command arguments separated with space
[ is a test command. So it requires space.
It's worth noting that [ is also used in glob matching, which can get you into trouble.
$ echo [12345]
[12345]
$ echo oops >3
$ echo [12345]
3
I was trying to write a Bash script that uses an if statement.
if[$CHOICE -eq 1];
The script was giving me errors until I gave a space before and after [ and before ] as shown below:
if [ $CHOICE -eq 1 ];
My question here is, why is the space around the square brackets so important in Bash?
Once you grasp that [ is a command, a whole lot becomes clearer!
[ is another way to spell "test".
help [
However while they do exactly the same, test turns out to have a more detailed help page. Check
help test
...for more information.
Furthermore note that I'm using, by intention, help test and not man test. That's because test and [ are shell builtin commands nowadays. Their feature set might differ from /bin/test and /bin/[ from coreutils which are the commands described in the man pages.
From another question:
A bit of history: this is because '[' was historically not a shell-built-in but a separate executable that received the expresson as arguments and returned a result. If you didn't surround the '[' with space, the shell would be searching $PATH for a different filename (and not find it) . – Andrew Medico Jun 24 '09 at 1:13
[ is a command and $CHOICE should be an argument, but by doing [$CHOICE (without any space between [ and $CHOICE) you are trying to run a command named [$CHOICE. The syntax for command is:
command arguments separated with space
[ is a test command. So it requires space.
It's worth noting that [ is also used in glob matching, which can get you into trouble.
$ echo [12345]
[12345]
$ echo oops >3
$ echo [12345]
3
I have something similar in a script I'm writing:
CMD="/path/to/cmd,there.sh"
TMP="${CMD##*/}"
echo "${TMP%%,*}"
Is there a way to nest the substring removals in line 2 & 3, or produce the same result in one-line, in pure bash, without going out to another program? The length of ${CMD} is not static. To be clear, I want the output to be simply "cmd".
I've tried the below, with various forms of brackets and quotations, but get a syntax error. This is something (I think) was allowed but isn't in new versions of Bash.
echo "${${CMD##*/}%%,*}"
Unfortunately, no, it's not possible to combine or nest string operations in bash.
With bash:
[[ $CMD =~ .*/([^,]*) ]] && echo ${BASH_REMATCH[1]}
Shell parameter substitution is primitive in that they don't provide functionalities like nesting. However, nobody prevents you from doing a sed thing here.
cmd="/path/to/cmd,there.sh" # Use lower-case identifiers for user variables
cmd=$(sed -E 's#^.*/([^,]+),.*$#\1#' <<<"$cmd")
The <<< enables the use of herestrings in bash.
I've found that zsh actually supports nested string operations, so I actually switched the interpreter to zsh for my script and the below works fine:
echo "${${CMD##*/}%%,*}"
If you want to write the script "in one line", just use ; or && to indicate the end of a line instead of a line-break:
CMD="/path/to/cmd,there.sh"; TMP="${CMD##*/}"; echo "${TMP%%,*}"
or
CMD="/path/to/cmd,there.sh" && TMP="${CMD##*/}" && echo "${TMP%%,*}"
A more elaborate answer about combining commands can be found below this question.
Disclaimer:
I understand that it is debatable whether or not this is a one-liner. But if you are visiting this question looking for a way to throw this in bash, it may answer your question regardless.
I was trying to write a Bash script that uses an if statement.
if[$CHOICE -eq 1];
The script was giving me errors until I gave a space before and after [ and before ] as shown below:
if [ $CHOICE -eq 1 ];
My question here is, why is the space around the square brackets so important in Bash?
Once you grasp that [ is a command, a whole lot becomes clearer!
[ is another way to spell "test".
help [
However while they do exactly the same, test turns out to have a more detailed help page. Check
help test
...for more information.
Furthermore note that I'm using, by intention, help test and not man test. That's because test and [ are shell builtin commands nowadays. Their feature set might differ from /bin/test and /bin/[ from coreutils which are the commands described in the man pages.
From another question:
A bit of history: this is because '[' was historically not a shell-built-in but a separate executable that received the expresson as arguments and returned a result. If you didn't surround the '[' with space, the shell would be searching $PATH for a different filename (and not find it) . – Andrew Medico Jun 24 '09 at 1:13
[ is a command and $CHOICE should be an argument, but by doing [$CHOICE (without any space between [ and $CHOICE) you are trying to run a command named [$CHOICE. The syntax for command is:
command arguments separated with space
[ is a test command. So it requires space.
It's worth noting that [ is also used in glob matching, which can get you into trouble.
$ echo [12345]
[12345]
$ echo oops >3
$ echo [12345]
3
I've just recently started programming scala, and in the book "Programming in Scala"(www.artima.com/pins1ed) the following method of executing scala scripts in linux is presented:
#!/bin/sh
exec scala "$0" "$#"
!#
// Say hello to the first argument
println("Hello, "+ args(0) +"!")
Now I've been using linux for a long time, but bash scripting is not my speciality. Now I can guess how this type of scrpt works(and it works beautifully), but I was wondering what do the !# and $# do exactly.
Thanks in advance for all the help!
Beautiful indeed. $0 and "$#" are positional paramters ($0 = command itself just like argv[0] in C, and argv[1]+ for "$#"), whereas #!* tells the shell, and sometimes the kernel if it recognizes it which program to execute for the file.
The thing that happens here actually is that the shell opens the script for input reading but on the point of exec, it transfers the input to scala, but scala wouldn't have to read it again from the beginning since the file descriptor is still open and so scala continues reading on the next line.
Rarely do I see scripts that do that with apparent and simple presentation of how it functions.
Note that exec replaces the process of the shell running the script and so it's like the shell becomes scala but scala would have the environment variable and opened handlers as the same.
UPDATE
Looks like I was wrong. Scala itself reads the whole but skips what it could see as header lines to it. So this is the real purpose of !#:
Script files may have an optional header that is ignored if present. There are two ways to format the header: either beginning with #! and ending with !#, or beginning with ::#! and ending with ::!#.
!# doesn't have anything to do with Bash. It's part of the Scala Language. It separates a non-Scala header from Scala Code in Script Mode.
"$#" represents all the script arguments.
You asked about what "$#" does exactly. It passes the arguments to the script in a non-word-splitting manner. Let's see some examples:
$ cat echowrap
#!/bin/sh
set -x
echo $*
echo $#
echo "$#"
$ ./echowrap oneword 'two words'
+ echo oneword two words
oneword two words
+ echo oneword two words
oneword two words
+ echo oneword 'two words'
oneword two words
In the first example, $* has split the input args so that echo sees three words.The second example $# behaves identically. The third example "$#" does not undergo word-splitting, therefore echo sees the same 2 args as were originally passed.
Consider a more useful example; if you called your script as
$ ./scalascript 'Joe Bloggs'
then try changing "$#" into $# or $*, the shell will pass two arguments to scala, scala will see args(0) and args(1), and the output of the test program will be different.