Special symbols in Linux - linux

what is the purpose of $1? while executing java file, we will give it as command line argument. To which it will refer?

You gave a little context here, but I thinks it's shell argument.
ARGUMENTS
If arguments remain after option processing, and neither the -c nor the -s option has been supplied, the first argument is assumed to be the name of a file containing shell commands. If bash is invoked in this fashion, $0 is set to the name of the file, and the positional parameters are set to the remaining arguments. Bash reads and executes commands from this file, then exits. Bash's exit status is the exit status of the last command executed in the script. If no commands are executed, the exit status is 0. An attempt is first made to open the file in the current directory, and, if no file is found, then the shell searches the directories in PATH for the script.
For more detailed, try man bash.
EXAMPLE
$ cat ./test.sh
#!/bin/bash
echo $0
echo $#
$ ./test.sh hello world
./test.sh
hello world

Related

Executing `sh -c` in a bash script

I have a test.sh file which takes as a parameter a bash command, it does some logic, i.e. setting and checking some env vars, and then executes that input command.
#!/bin/bash
#Some other logic here
echo "Run command: $#"
eval "$#"
When I run it, here's the output
% ./test.sh echo "ok"
Run command: echo ok
ok
But the issue is, when I pass something like sh -c 'echo "ok"', I don't get the output.
% ./test.sh sh -c 'echo "ok"'
Run command: sh -c echo "ok"
%
So I tried changing eval with exec, tried to execute $# directly (without eval or exec), even tried to execute it and save the output to a variable, still no use.
Is there any way to run the passed command in this format and get the ourput?
Use case:
The script is used as an entrypoint for the docker container, it receives the parameters from docker CMD and executes those to run the container.
As a quickfix I can remove the sh -c and pass the command without it, but I want to make the script reusable and not to change the commands.
TL;DR:
This is a typical use case (perform some business logic in a Docker entrypoint script before running a compound command, given at command line) and the recommended last line of the script is:
exec "$#"
Details
To further explain this line, some remarks and hyperlinks:
As per the Bash user manual, exec is a POSIX shell builtin that replaces the shell [with the command supplied] without creating a new process.
As a result, using exec like this in a Docker entrypoint context is important because it ensures that the CMD program that is executed will still have PID 1 and can directly handle signals, including that of docker stop (see also that other SO answer: Speed up docker-compose shutdown).
The double quotes ("$#") are also important to avoid word splitting (namely, ensure that each positional argument is passed as is, even if it contains spaces). See e.g.:
#!/usr/bin/env bash
printargs () { for arg; do echo "$arg"; done; }
test0 () {
echo "test0:"
printargs $#
}
test1 () {
echo "test1:"
printargs "$#"
}
test0 /bin/sh -c 'echo "ok"'
echo
test1 /bin/sh -c 'echo "ok"'
test0:
/bin/sh
-c
echo
"ok"
test1:
/bin/sh
-c
echo "ok"
Finally eval is a powerful bash builtin that is (1) unneeded for your use case, (2) and actually not advised to use in general, in particular for security reasons. E.g., if the string argument of eval relies on some user-provided input… For details on this issue, see e.g. https://mywiki.wooledge.org/BashFAQ/048 (which recaps the few situations where one would like to use this builtin, typically, the command eval "$(ssh-agent -s)").

Variable assignment exits shell script

I have simple shell script that tries to find out if a specific docker container is running. In the shell script I have the follwoing line;
RUNNING_CONTAINERS=$(docker ps -a | grep ${IMAGE_NAME})
If the grep returns no results, the shell script exits right there. How would I write my script to make sure the script continues to execute even if the result of the grep is empty?
The reason of this is the existence of set -e somewhere in the code, which makes your script exit as soon as a command returns a non-zero status. In this case, grep because it did not find any match.
As read in The Bash reference manual -> The set builtin
-e
Exit immediately if a pipeline (see Pipelines), which may consist of a
single simple command (see Simple Commands), a list (see Lists), or a
compound command (see Compound Commands) returns a non-zero status.
The shell does not exit if the command that fails is part of the
command list immediately following a while or until keyword, part of
the test in an if statement, part of any command executed in a && or
|| list except the command following the final && or ||, any command
in a pipeline but the last, or if the command’s return status is being
inverted with !. If a compound command other than a subshell returns a
non-zero status because a command failed while -e was being ignored,
the shell does not exit. A trap on ERR, if set, is executed before the
shell exits.
Also, from man grep:
EXIT STATUS
Normally the exit status is 0 if a line is selected, 1 if no lines
were selected, and 2 if an error occurred. However, if the -q or
--quiet or --silent is used and a line is selected, the exit status is 0 even if an error occurred.
So grep doesn't find anything and returns a non-zero exit status. Then, set -e captures it and sees it does not come from an "exception" (if, while... as mentioned in the reference), neither it is before the last command in the pipeline, so it exits.
Test
Let's create a very basic script:
$ cat a.sh
#!/bin/bash
set -e
echo "hello"
grep "hello" a
echo "bye"
And generate an empty a file:
$ touch a
If we run it we see it exits when grep doesn't return any result:
$ ./a.sh
hello
However, if we remove the set -e line, it goes through to the end of the file:
$ ./a.sh
hello
bye
See also it doesn't fail if grep is not the last element in the pipeline:
$ cat a.sh
#!/bin/bash
set -e
echo "hello"
grep "hello" a | echo "he"
echo "bye"
$ ./a.sh
hello
he
bye

read more than one parameters right after the command in bash

I am making a bash script and I want it to be just one line, meaning it will not have any interaction with the users and the parameters will be on the same line as the command. Once the user clicks return, it will output the result.
Right now, I have something that looks like this:
#! \bin\bash
read $1 $2
do something with $1 and $2
However, if I name my script "test" when I type in test at the beginning of the command line, I will have to type enter for the rest of the script to be executed. How should I modify it so that I can run the entire thing on just one line?
The standard way to pass parameters to a script is not with read (which actively waits for input from stdin), but just to call your script with the parameters on the same line:
./my_script.sh param1 param2
Then inside the script, you can access these parameters using $1, $2, etc. Example (note also the first line - this describes what shell should be used to run the script, and should be a valid path - ie /bin/bash, not backslashes):
#!/bin/bash
echo "First: $1 Second: $2"
Then call the script:
$ ./my_script.sh Hello There
First: Hello Second: There
What you probably need is this :
You script name test.sh contains the following:
#!/bin/bash
echo "$1 $2"
Then, change permission so that you can execute the script on the command line :
chmod u+x test.sh
and run the script with arguments (two in this case) :
./test.sh tik tak
will return
tik tak

Shell Script and Command Line Discrepancies

I was wondering if there were specific permissions that were associated with a shell script or if some variable references are taken as being syntactically different.
I tried my short renaming script below:
#!/bin/bash
echo "Starting Renaming Script"
for file in ./*
do
rename=$(echo $file | sed 's/\(img_\)\([0-9]*-[0-9]*\)-\([0-9]*\)_\([0-9]*\).jpg/newyears_20\3-\2_0\4.jpg/')
mv $file $rename
done
All it does is rename a few files, but I noticed that it would work on the command line, but not in the shell script when I ran sh rename.sh
I got the error
rename.sh: syntax error at line 7: `rename=$' unexpected
Is variable assignment handled differently in the shell than on the command line?
Different shells handle commands differently. Your script is a bash script (as identified on the first line #!/bin/bash), therefore it needs to be run by bash, not sh.
bash rename.sh

Concatenate strings inside bash script (different behaviour from shell)

I'm trying some staff that is working perfectly when I write it in the regular shell, but when I include it in a bash script file, it doesn't.
First example:
m=`date +%m`
m_1=$((m-1))
echo $m_1
This gives me the value of the last month (actual minus one), but doesn't work if its executed from a script.
Second example:
m=6
m=$m"t"
echo m
This returns "6t" in the shell (concatenates $m with "t"), but just gives me "t" when executing from a script.
I assume all these may be answered easily by an experienced Linux user, but I'm just learning as I go.
Thanks in advance.
Re-check your syntax.
Your first code snippet works either from command line, from bash and from sh since your syntax is valid sh. In my opinion you probably have typos in your script file:
~$ m=`date +%m`; m_1=$((m-1)); echo $m_1
4
~$ cat > foo.sh
m=`date +%m`; m_1=$((m-1)); echo $m_1
^C
~$ bash foo.sh
4
~$ sh foo.sh
4
The same can apply to the other snippet with corrections:
~$ m=6; m=$m"t"; echo $m
6t
~$ cat > foo.sh
m=6; m=$m"t"; echo $m
^C
~$ bash foo.sh
6t
~$ sh foo.sh
6t
Make sure the first line of your script is
#!/bin/bash
rather than
#!/bin/sh
Bash will only enable its extended features if explicitly run as bash. If run as sh, it will operate in POSIX compatibility mode.
First of all, it works fine for me in a script, and on the terminal.
Second of all, your last line, echo m will just output "m". I think you meant "$m"..

Resources