I have the following line at the first line in my script file:
#!/bin/sh
So I'm using csh.(?)
I wanto assign the output of the following to an environment variable:
echo $MYUSR | awk '{print substr($0,4)}'
I try:
set $MYVAR = echo $MYUSR | awk '{print substr($0,4)}'
But it doesn't work,
How can I do it? I want to do it in a sh file.
Your script should look like
#!/bin/csh
set MYVAR = `echo $MYUSR | awk '{print substr($0,4)}'`
echo $MYVAR
I don't have a way to test this right now, let me now if it doesn't work.
If you've inherited the basis of your script from someone else, with the #!/bin/sh,
then you have to find out if /bin/sh is really the bourne shell, or if it is a link to /bin/bash
You can tell that by doing
ls -l /bin/sh /bin/bash
if you get back information on files where the size is exactly the same, the you're really using bash, but called as /bin/sh
So try these 2 solutions
MYVAR=$(echo $MYUSR | awk '{print substr($0,4)}')
echo $MYVAR
AND
MYVAR=``echo $MYUSR | awk '{print substr($0,4)}``
echo $MYVAR
# arg!! only one pair of enclosing back-ticks needed,
# can't find the secret escape codes to make this look exactly right.
in all cases (csh) included, the back-ticks AND the $( ... ) are known as command substitution.
What every output comes from running the command inside, is substituted into the command line AND then the whole command is executed.
I hope this helps.
if it's /bin/sh it's bourne shell or bash, and use back quotes to execute something and this to assign that...
MYVAR=`echo $MYUSR | awk ...`
That script first line indicates that it should be interpreted by the Bourne shell (sh), not csh. Change it to
#!/bin/csh
The first line of your code shows clearly you are not using a csh. You are using a plain sh environment/shell. You have 2 options:
Either change the first line to #!/bin/csh OR
Keeping first line unchanged, update the code for setting the variable.
MYVAR=`echo $MYUSR | awk '{print substr($0,4)}`
echo $MYVAR
Let me know, if you get any error.
Related
So I am trying to make a script that contains egrep and accepts a numeric variable
#!/bin/bash
var=$1
list="egrep "^.{$var}$ /usr/share/dict/words"
cat list
For example, if var is 5, I would like this script to print out every line with 5 characters. For some reason the script does not do that. Help would be greatly appreciated!
Your script doesn't work because there are several problems with these lines:
list="egrep "^.{$var}$ /usr/share/dict/words"
cat list
The first line isn't complete, it's missing a closing quote,
Even if you fixed it, you're assigning a literal string to list, not the output of a command,
RE and filename should be separated
cat doesn't print a variable's content, echo does that.
So:
#!/bin/bash
var="$1"
list="$(egrep '^.{'"$var"'}$' /usr/share/dict/words)"
echo "$list"
should work.
Or even better, you can use just an awk command:
awk 'length==5' /usr/share/dict/words
with $1 or any other variable:
awk -v n="$1" 'length==n' /usr/share/dict/words
with this grep it shows a comand I used:
echo `history | grep "ssh root" | head -1| cut -c6-`
with this output:
ssh root#107.170.70.100
I want the output to directly execute as the command instead of printed.
How can I do it?
In principle, this can be done by using the $() format, so
$(history | grep "ssh root" | head -1| cut -c6-)
should do what you ask for. However, I don't think that it is advisable to do so, as this will automatically execute the command that results from your grep, so if you did a mistake, a lot of bad things can happen. Instead I suggest reviewing your result before re-executing. bash history has a lot of nice shortcuts to deal with these kind of things. As an example, imagine:
> history | grep "ssh root"
756 ssh root#107.170.70.100
you can call this command on line 756 easily by typing
!756
It's definitely much safer. Hope this helps.
Ideally you'd be using the $(cmd) syntax rather than the `cmd` syntax. This makes it easier to nest subshells as well as keep track of what's going on.
That aside, if you remove the echo statement it will run the script:
# Prints out ls
echo $( echo ls )
# Runs the ls command
$( echo ls )
Use eval.
$ eval `history | grep "ssh root" | head -1| cut -c6-`
From eval command in Bash and its typical uses:
eval takes a string as its argument, and evaluates it as if you'd typed that string on a command line.
And the Bash Manual (https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html#Bourne-Shell-Builtins)
eval
eval [arguments]
The arguments are concatenated together into a single command, which is then read and executed, and its exit status returned as the exit status of eval. If there are no arguments or only empty arguments, the return status is zero.
I have a shell script of more than 1000 lines, i would like to check if all the commands used in the script are installed in my Linux operating system.
Is there any tool to get the list of Linux commands used in the shell script?
Or how can i write a small script which can do this for me?
The script runs successfully on the Ubuntu machine, it is invoked as a part of C++ application. we need to run the same on a device where a Linux with limited capability runs. I have identified manually, few commands which the script runs and not present on Device OS. before we try installing these commands i would like to check all other commands and install all at once.
Thanks in advance
I already tried this in the past and got to the conclusion that is very difficult to provide a solution which would work for all scripts. The reason is that each script with complex commands has a different approach in using the shells features.
In case of a simple linear script, it might be as easy as using debug mode.
For example: bash -x script.sh 2>&1 | grep ^+ | awk '{print $2}' | sort -u
In case the script has some decisions, then you might use the same approach an consider that for the "else" cases the commands would still be the same just with different arguments or would be something trivial (echo + exit).
In case of a complex script, I attempted to write a script that would just look for commands in the same place I would do it myself. The challenge is to create expressions that would help identify all used possibilities, I would say this is doable for about 80-90% of the script and the output should only be used as reference since it will contain invalid data (~20%).
Here is an example script that would parse itself using a very simple approach (separate commands on different lines, 1st word will be the command):
# 1. Eliminate all quoted text
# 2. Eliminate all comments
# 3. Replace all delimiters between commands with new lines ( ; | && || )
# 4. extract the command from 1st column and print it once
cat $0 \
| sed -e 's/\"/./g' -e "s/'[^']*'//g" -e 's/"[^"]*"//g' \
| sed -e "s/^[[:space:]]*#.*$//" -e "s/\([^\\]\)#[^\"']*$/\1/" \
| sed -e "s/&&/;/g" -e "s/||/;/g" | tr ";|" "\n\n" \
| awk '{print $1}' | sort -u
the output is:
.
/
/g.
awk
cat
sed
sort
tr
There are many more cases to consider (command substitutions, aliases etc.), 1, 2 and 3 are just beginning, but they would still cover 80% of most complex scripts.
The regular expressions used would need to be adjusted or extended to increase precision and special cases.
In conclusion if you really need something like this, then you can write a script as above, but don't trust the output until you verify it yourself.
Add export PATH='' to the second line of your script.
Execute your_script.sh 2>&1 > /dev/null | grep 'No such file or directory' | awk '{print $4;}' | grep -v '/' | sort | uniq | sed 's/.$//'.
If you have a fedora/redhat based system, bash has been patched with the --rpm-requires flag
--rpm-requires: Produce the list of files that are required for the shell script to run. This implies -n and is subject to the same limitations as compile time error checking checking; Command substitutions, Conditional expressions and eval builtin are not parsed so some dependencies may be missed.
So when you run the following:
$ bash --rpm-requires script.sh
executable(command1)
function(function1)
function(function2)
executable(command2)
function(function3)
There are some limitations here:
command and process substitutions and conditional expressions are not picked up. So the following are ignored:
$(command)
<(command)
>(command)
command1 && command2 || command3
commands as strings are not picked up. So the following line will be ignored
"/path/to/my/command"
commands that contain shell variables are not listed. This generally makes sense since
some might be the result of some script logic, but even the following is ignored
$HOME/bin/command
This point can however be bypassed by using envsubst and running it as
$ bash --rpm-requires <(<script envsubst)
However, if you use shellcheck, you most likely quoted this and it will still be ignored due to point 2
So if you want to use check if your scripts are all there, you can do something like:
while IFS='' read -r app; do
[ "${app%%(*}" == "executable" ] || continue
app="${app#*(}"; app="${app%)}";
if [ "$(type -t "${app}")" != "builtin" ] && \
! [ -x "$(command -v "${app}")" ]
then
echo "${app}: missing application"
fi
done < <(bash --rpm-requires <(<"$0" envsubst) )
If your script contains files that are sourced that might contain various functions and other important definitions, you might want to do something like
bash --rpm-requires <(cat source1 source2 ... <(<script.sh envsubst))
Based #czvtools’ answer, I added some extra checks to filter out bad values:
#!/usr/bin/fish
if test "$argv[1]" = ""
echo "Give path to command to be tested"
exit 1
end
set commands (cat $argv \
| sed -e 's/\"/./g' -e "s/'[^']*'//g" -e 's/"[^"]*"//g' \
| sed -e "s/^[[:space:]]*#.*\$//" -e "s/\([^\\]\)#[^\"']*\$/\1/" \
| sed -e "s/&&/;/g" -e "s/||/;/g" | tr ";|" "\n\n" \
| awk '{print $1}' | sort -u)
for command in $commands
if command -q -- $command
set -a resolved (realpath (which $command))
end
end
set resolved (string join0 $resolved | sort -z -u | string split0)
for command in $resolved
echo $command
end
I am trying to just echo a command within my bash script code.
OVERRUN_ERRORS="$ifconfig | egrep -i "RX errors" | awk '{print $7}'"
echo ${OVERRUN_ERRORS}
however it gives me an error and the $7 does not show up in the command. I have to store it in a variable, because I will process the output (OVERRUN_ERRORS) at a later point in time. What's the right syntax for doing this? Thanks.
On Bash Syntax
foo="bar | baz"
...is assigning the string "bar | baz" to the variable named foo; it doesn't run bar | baz as a pipeline. To do that, you want to use command substitution, in either its modern $() syntax or antiquated backtick-based form:
foo="$(bar | baz)"
On Storing Code For Later Execution
Since your intent isn't clear in the question --
The correct way to store code is with a function, whereas the correct way to store output is in a string:
# store code in a function; this also works with pipelines
get_rx_errors() { cat /sys/class/net/"$1"/statistics/rx_errors; }
# store result of calling that function in a string
eth0_errors="$(get_rx_errors eth0)"
sleep 1 # wait a second for demonstration purposes, then...
# compare: echoing the stored value, vs calculating a new value
echo "One second ago, the number of rx errors was ${eth0_errors}"
etho "Right now, it is $(get_rx_errors eth0)"
See BashFAQ #50 for an extended discussion of the pitfalls of storing code in a string, and alternatives to same. Also relevant is BashFAQ #48, which describes in detail the security risks associated with a eval, which is often suggested as a workaround.
On Collecting Interface Error Counts
Don't use ifconfig, or grep, or awk for this at all -- just ask your kernel for the number you want:
#!/bin/bash
for device in /sys/class/net/*; do
[[ -e $device/statistics/rx_errors ]] || continue
rx_errors=$(<"${device}/statistics/rx_errors")
echo "Number of rx_errors for ${device##*/} is $rx_errors"
done
Use $(...) to capture the output of a command, not double quotes.
overrun_errors=$(ifconfig | egrep -i "RX errors" | awk '{print $7}')
Your double quotes around RX errors are a problem. Try;
OVERRUN_ERRORS="$ifconfig | egrep -i 'RX errors' | awk '{print $7}'"
To see the commands as they are executing, you can use
set -v
or
set -x
For example;
set -x
OVERRUN_ERRORS="$ifconfig | egrep -i 'RX errors' | awk '{print $7}'"
set +x
Here's a MWE:
#!/bin/bash
INFILE=$1
echo `echo $INFILE | awk '{print(substr($0,8,3))}'`
PATH=${INFILE%/*}
echo `echo $INFILE | awk '{print(substr($0,8,3))}'`
exit
Apparently the first awkcommand runs fine, but in the second command bash doesn't recognize awkanymore!
This is what I get running it (assuming that f_mwe.sh is the name of the file):
$ ./f_mwe.sh /home/something/path/this_is_the_name.txt
ome
./f_mwe.sh: line 31: awk: command not found
$
I have tried defining /bin/sh and ksh at the beginning also but got the same results. I have no idea what's causing this.
Any help is appreciated.
You are overwriting the PATH variable and not appending to it I believe. You should append to the PATH variable.