Need help in this unix command - linux

I am trying to fetch the latest file in a directory and get the time since it is modified. Using the below command but getting error. Can someone tell me what I am doing wrong here?
And is there any simplified version for this?
NOW=`date +%s`;
FILE=`ls -lpt /tmp/app/test/*.txt | head -n 1 | awk '{print $9}'`;
Time=`stat -c %Y ${FILE}`;
DIFF=`${NOW} - ${Time}`;
echo ${DIFF}
-bash: 1552214130: command not found

This is wrong:
DIFF=`${NOW} - ${Time}`;
Backticks mean to execute what's contained as a command, and then substitute the output into the command. So this will try to use the value of ${NOW} as the name of a command to execute. But it's just a numeric timestamp, not a command.
In order to perform calculations in bash use $(( expression )), not backticks.
DIFF=$((NOW - Time))
BTW, you should not use all-capital names for your variables. By convention those are reserved for environment variables.

Related

Unable to get the absolute value of command output

So I wanted to make a simple script to keep checking the CPU temperature of my RasPi, which is stored in /sys/class/thermal/thermal_zone0/temp , and hence cat /sys/class/thermal/thermal_zone0/temp would give the temp, but like this :
cat /sys/class/thermal/thermal_zone0/temp
38459
which essentially means 38.459 degree Celsius.
I was unable to format the output to get 38.594 °C
My code:
tempT="$(cat /sys/class/thermal/thermal_zone0/temp)"
tempC=$($tempT / 1000)
echo "$tempC °C"
The error I get:
-bash: 38459: command not found
°C
Thanks
The simplest would be to use awk.
awk '{print $1/1000}' /sys/class/thermal/thermal_zone0/temp
or with some more control with printf
awk '{printf "%.3f\n", $1/1000}' /sys/class/thermal/thermal_zone0/temp
The error you are seeing comes from that you used $( ...), which is a command substitution and tries to run the command inside. So when you do:
$($tempT / 1000)
First $tempT expands to 38459 and then shell tries to run a command named 38459 with two arguments / and 1000. So you see the message 38459: Command not found. Use $((...)) for arithmetic expansion, but shells do not implement floating point arithmetic so you have to use other tools like awk or bc.
I'd use bc if it is available on your system.
$ CELSIUS=$(bc -l <<< $(cat /sys/class/thermal/thermal_zone0/temp)/1000)
$ echo $CELSIUS
25.00000000000000000000
TempC=$($tempT / 1000);
Resolves to:
TempC=$(38459 / 1000);
And bash treats $(...) as a command to be passed into a subshell, so it tries to run the executable 38455, which it can't find, and hence complains.
I would use bc, as #kinezan suggested, though I personally prefer the following convention:
TempC=$(echo "scale=3; $tempT / 1000" | bc)
which outputs 38.459

how to pass asterisk into ls command inside bash script

Hi… Need a little help here…
I tried to emulate the DOS' dir command in Linux using bash script. Basically it's just a wrapped ls command with some parameters plus summary info. Here's the script:
#!/bin/bash
# default to current folder
if [ -z "$1" ]; then var=.;
else var="$1"; fi
# check file existence
if [ -a "$var" ]; then
# list contents with color, folder first
CMD="ls -lgG $var --color --group-directories-first"; $CMD;
# sum all files size
size=$(ls -lgGp "$var" | grep -v / | awk '{ sum += $3 }; END { print sum }')
if [ "$size" == "" ]; then size="0"; fi
# create summary
if [ -d "$var" ]; then
folder=$(find $var/* -maxdepth 0 -type d | wc -l)
file=$(find $var/* -maxdepth 0 -type f | wc -l)
echo "Found: $folder folders "
echo " $file files $size bytes"
fi
# error message
else
echo "dir: Error \"$var\": No such file or directory"
fi
The problem is when the argument contains an asterisk (*), the ls within the script acts differently compare to the direct ls command given at the prompt. Instead of return the whole files list, the script only returns the first file. See the video below to see the comparation in action. I don't know why it behaves like that.
Anyone knows how to fix it? Thank you.
Video: problem in action
UPDATE:
The problem has been solved. Thank you all for the answers. Now my script works as expected. See the video here: http://i.giphy.com/3o8dp1YLz4fIyCbOAU.gif
The asterisk * is expanded by the shell when it parses the command line. In other words, your script doesn't get a parameter containing an asterisk, it gets a list of files as arguments. Your script only works with $1, the first argument. It should work with "$#" instead.
This is because when you retrieve $1 you assume the shell does NOT expand *.
In fact, when * (or other glob) matches, it is expanded, and broken into segments by $IFS, and then passed as $1, $2, etc.
You're lucky if you simply retrieved the first file. When your first file's path contains spaces, you'll get an error because you only get the first segment before the space.
Seriously, read this and especially this. Really.
And please don't do things like
CMD=whatever you get from user input; $CMD;
You are begging for trouble. Don't execute arbitrary string from the user.
Both above answers already answered your question. So, i'm going a bit more verbose.
In your terminal is running the bash interpreter (probably). This is the program which parses your input line(s) and doing "things" based on your input.
When you enter some line the bash start doing the following workflow:
parsing and lexical analysis
expansion
brace expansion
tidle expansion
variable expansion
artithmetic and other substitutions
command substitution
word splitting
filename generation (globbing)
removing quotes
Only after all above the bash
will execute some external commands, like ls or dir.sh... etc.,
or will do so some "internal" actions for the known keywords and builtins like echo, for, if etc...
As you can see, the second last is the filename generation (globbing). So, in your case - if the test* matches some files, your bash expands the willcard characters (aka does the globbing).
So,
when you enter dir.sh test*,
and the test* matches some files
the bash does the expansion first
and after will execute the command dir.sh with already expanded filenames
e.g. the script get executed (in your case) as: dir.sh test.pas test.swift
BTW, it acts exactly with the same way for your ls example:
the bash expands the ls test* to ls test.pas test.swift
then executes the ls with the above two arguments
and the ls will print the result for the got two arguments.
with other words, the ls don't even see the test* argument - if it is possible - the bash expands the wilcard characters. (* and ?).
Now back to your script: add after the shebang the following line:
echo "the $0 got this arguments: $#"
and you will immediatelly see, the real argumemts how your script got executed.
also, in such cases is a good practice trying to execute the script in debug-mode, e.g.
bash -x dir.sh test*
and you will see, what the script does exactly.
Also, you can do the same for your current interpreter, e.g. just enter into the terminal
set -x
and try run the dir.sh test* = and you will see, how the bash will execute the dir.sh command. (to stop the debug mode, just enter set +x)
Everbody is giving you valuable advice which you should definitely should follow!
But here is the real answer to your question.
To pass unexpanded arguments to any executable you need to single quote them:
./your_script '*'
The best solution I have is to use the eval command, in this way:
#!/bin/bash
cmd="some command \"with_quetes_and_asterisk_in_it*\""
echo "$cmd"
eval $cmd
The eval command takes its arguments and evaluates them into the command as the shell does.
This solves my problem when I need to call a command with asterisk '*' in it from a script.

Using diff to compare a file with a variable

In diff command am getting following error. Kindly assist how can I specify I want to see difference between a file and a variable:
$ current_unavail=ranjith
$ cat /tmp/ran
ranjith
$ test=$(cat /tmp/ran)
error which I am getting
$ diff `$current_unavail` `$test`
diff: missing operand after `diff'
diff: Try `diff --help' for more information.
You're using the wrong kind of quotes. Assuming that $current_unavail and $test are two shell variables, each containing the name of a file, you should be doing this:
diff "$current_unavail" "$test"
Backticks ` are used for command substitutions (like a=`cmd`), although the preferred syntax is a=$(cmd).
To compare a file /tmp/ran with a variable $current_unavail, you can do this:
diff /tmp/ran <(echo "$current_unavail")
diff works with file descriptors, not variables. But in bash you can use a process substitution <( ... ) to create a temporary file descriptor from the result of executing a command.

BASH - xargs command not found

I am trying to create a BASH script that will run a command for me. This is an example of one of the commands:
function systemStart {
./ORBMarkerDetection $1 $2 $3 | xargs -n3 java -jar ../../system/layers/out/artifacts/layers_jar/layers.jar
}
But when this is ran I am getting the error (referring to the above line):
./runActivities.sh: line 7: xargs: command not found
I am able to run this command in the terminal with success so I am not sure why this will not run within a BASH script?
I am calling the function like so:
systemStart $PATH/1.1/cupCupboard.png $PATH/1.1/kitchenDoor.png $PATH/1.1/tap.png
You are apparently using the variable name PATH for your own purposes, but you can't do that -- PATH is a reserved variable, and changing it will cause the shell to not find commands (not just xargs but basically any command).
In general, you should avoid using uppercase variable names; then you can be sure yours will never conflict with a built-in shell variable.
(You may need to put the fully-qualified path in your script?)
The command which can tell you the fully-qualified path for things.
robert#debian:~$ which xargs
/usr/bin/xargs
locate can also tell you the location of files
Lastly, then a brute-force full filesystem search using find:
robert#debian:~$ find / -name "xargs" 2> /dev/null
/usr/bin/xargs

Setting an environment variable in csh

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.

Resources