How to print Shell command literally? - shell

In a shell script, is there a way to print command in this literal form after all variables are replaced with real value. For example, mkdir -p $HOME/src, before this command is executed, print something like mkdir -p /home/user/src. Thanks.

Try set -x argument and check this

Related

How to pass argument to a shell command in shell script from terminal

i am writing a shell script practice.sh. I want to give my first argument $1 from command line to ls command in script.e.g
if I run my script in terminal $bash practice.sh *.mp3
the argument *.mp3
I want to use for ls command
#!/bin/bash
output=$ls $1
it doesn't work
any help?
The obvious answer for what you say you want is just
#!/bin/bash
ls "$1"
which will run ls, passing it (just) the first argument to the script.
However, you also say you want to run this like: practice.sh *.mp3 which runs the script with many arguments (not just one) -- the *.mp3 will be expanded to be all the of the .mp3 files in the current directory. For that, you likely want something more like
#!/bin/bash
ls "$#"
which will pass all of the arguments to your script (however many there are) to the ls command.
These scripts will just run ls with its stdout connected to whatever your script has its stdout connceted to, so the output will (likely) just appear on your terminal. If you instead want to capture the output of the ls command (so you can do something else with it), you need something like
#!/bin/bash
output=$(ls "$#")
which will run ls with all the arguments, and capture the output in the variable $output. You can then do things with that variable.
Use shell expansion to record the output of the command in the variable output:
output=$(ls $1)
This will record the output of the command ls $1 in the variable output.
You can then use echo $output to print out your output.
You can read more about shell expansion in the GNU Bash reference manual.

Bash: Assign command to a variable

Below I have an example which confuses me a bit, any help would be appreciated.
I bind a normal command line command (ls) to a new variable. If I echo it it the output is just the command (ls) but if I just use the variable without echo i get the result of the command but why?? Is it because $listdir gets translated to ls so I just get the output? And if I use the echo command it will be interpreted as a string?
router#test:~/scripting$ listdir=ls
router#test:~/scripting$ echo "$listdir"
ls
----- VS ----
router#test:~/scripting$ $listdir
basicLoop.sh fileflood.sh .......
Thank you for any help!
By doing listdir=ls you literally assign a string "ls" to the $listdir variable. So if you run echo $listdir now it will just expand into echo ls, which (as you may have guessed) will just print "ls" onto a screen. If you want to store a result of a command into a variable you can wrap the command in `` or $() (eg. listdir=$(ls) or listdir=`ls`).
jarmusz#emacs~$ listdir=`ls`
jarmusz#emacs~$ echo "$listdir"
dls
docs
music
...
If you want to store just a name of the command and run it later you can do it like this:
jarmusz#emacs~$ listdir=ls
<some other commands...>
jarmusz#emacs~$ echo `$listdir`
dls docs music ...
In this example, echo `$listdir` will expand into echo `ls` and then into echo dls docs music...
When bash is interpreting the commands you feed to it, the first thing it will do is expand any expansions it is given. So when you give it $listdir by the time bash starts to execute the value it is given, all it knows is that it was given the value ls. It does not care where the value came from, only what the value is.
Lets look at the trace given after running set -x, which instructs bash to prints to stderr after expansion and before execution:
$> echo $listdir
+ echo ls
ls
$> $listdir
+ ls
file_0 file_1
As you can see, in the second line, bash will attempt to run the command ls just as if you have explicity called ls or even /usr/bin/ls
Edit
Expansion isn't the first step in in shell evaluation, see #Gordon Davisson's comment for details

Can I make "bash -x" not print the "+ " prefix?

When I invoke bash with the -x option, it prints each command before it is executed. However - the commands are printed with a "+ " prefix.
Is there a way to get it to print the commands, but without this prefix? With -x or without it?
You can customize the prefix when using bash -x by setting the variable PS4. So in your .bashrc, you could have something like this:
export PS4=''
Or, if you want to debug something, let it print the line number like this:
export PS4='[ $LINENO ] '
Insert as the first line :
set -v
then run it without -x

Executing the output as filename

In one of my Bash scripts, there's a point where I have a variable SCRIPT which contains the /path/to/an/exe, and what the script ultimately needs to do, is executing that executable. Therefore the last line of the script is
$($SCRIPT)
so that $SCRIPT is expanded to /path/to/an/exe, and $(/path/to/an/exe) executes the executable.
However, running shellcheck on the script generates this error:
In setscreens.sh line 7:
$($SCRIPT)
^--------^ SC2091: Remove surrounding $() to avoid executing output.
For more information:
https://www.shellcheck.net/wiki/SC2091 -- Remove surrounding $() to avoid e...
Is there a way I can rewrite that $($SCRIPT) in a more appropriate way? eval does not seem to be of much help here.
$($SCRIPT) indeed does not do what you think it does.
The outer $() will execute any commands inside the parenthesis and execute the result string.
The inner $SCRIPT will expand to the value of the SCRIPT variable and execute this string while splitting words on spaces/
If you want to execute the command contained into the SCRIPT variable, you just write as an example:
SCRIPT='/bin/ls'
"$SCRIPT" # Will execute /bin/ls
Now if you also need to handle arguments with your SCRIPT variable command call:
SCRIPT='/bin/ls'
"$SCRIPT" -l # Will execute /bin/ls -l
To also store or build arguments dynamically, you'd need an array instead of a string variable.
Example:
SCRIPT=(/bin/ls -l)
"${SCRIPT[#]}" # Will execute /bin/ls -l
SCRIPT+=(/etc) # Add /etc to the array
"${SCRIPT[#]}" # Will execute /bin/ls -l /etc
It worked for me with sh -c:
$ chrome="/opt/google/chrome/chrome"
$ sh -c "$chrome"
Opening in existing browser session.
It also passed the ShellCheck without any issues.
with bash, just use $SCRIPT:
cat <<'EOF' > test.sh
SCRIPT='echo aze rty'
$SCRIPT
EOF
bash test.sh
produce:
aze rty

Store last command in a variable in Bash

I'm want to store the most recent command in a variable. I tried the !!:p history expansion, it does get me the last command but I can't store it in a variable.
$ last=`!!:p`
last=`ls`
$ echo $last
$
Any help?
The fc command can be used to retrieve the previous command.
some_var=$(fc -nl -1)
Using !!:p only prints the last command, to execute the last command you would do !!.
$ mycmd="$(!!)"
$ sh "$mycmd"
That should do what you're looking for...
The solution given above does not work inside a script file:
some_var=$(fc -nl -1)
What I did was use trap option and store the last command in environment variable before the script is called and access the env variable in script.
Add this function in ~/.bashrc
function process_command() {
if [ "$BASH_COMMAND" != "$PROMPT_COMMAND" ];then
export myCMD=$BASH_COMMAND
fi
}
trap process_command DEBUG
Then use $myCMD variable in any script or anywhere in shell.

Resources