Using diff to compare a file with a variable - linux

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.

Related

Need help in this unix command

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.

Getting the most recent filename where the extension name is case *in*sensitive

I am trying to get the most recent .CSV or .csv file name among other comma separated value files where the extension name is case insensitive.
I am achieving this with the following command, provided by someone else without any explanation:
ls -t ~(i:*.CSV) | head -1
or
ls -t -- ~(i:*.CSV) | head -1
I have two questions:
What is the use of ~ and -- in this case? Does -- helps here?
How can I get a blank response when there is no .csv or .CSV file in
the folder? At the moment I get:
/bin/ls: cannot access ~(i:*.CSV): No such file or directory
I know I can test the exit code of the last command, but I was wondering maybe there is a --silent option or something.
Many thanks for your time.
PS: I made my research online quite thorough and I was unable to find an answer.
The ~ is just a literal character; the intent would appear to be to match filenames starting with ~ and ending with .csv, with i: being a flag to make the match case-insensitive. However, I don't know of any shell that supports that particular syntax. The closest thing I am aware of would be zsh's globbing flags:
setopt extended_glob # Allow globbing flags
ls ~(#i)*.csv
Here, (#i) indicates that anything after it should be matched without regard to case.
Update: as #baptistemm points out, ~(i:...) is syntax defined by ksh.
The -- is a conventional argument, supported by many commands, to mean that any arguments that follow are not options, but should be treated literally. For example, ls -l would mean ls should use the -l option to modify its output, while ls -- -l means ls should try to list a file named -l.
~(i:*.CSV) is to tell to shell (this is only supported apparently in ksh93) the enclosed text after : must be treated as insensitive, so in this example that could all these possibilites.
*.csv or
*.Csv or
*.cSv or
*.csV or
*.CSv or
*.CSV
Note this could have been written ls -t *.[CcSsVv] in bash.
To silent errors I suggest you to look for in this site for "standard error /dev/null" that will help.
I tried running commands like what you have in both bash and zsh and neither worked, so I can't help you out with that, but if you want to discard the error, you can add 2>/dev/null to the end of the ls command, so your command would look like the following:
ls -t ~(i:*.CSV) 2>/dev/null | head -1
This will redirect anything written to STDERR to /dev/null (i.e. throw it out), which, in your case, would be /bin/ls: cannot access ~(i:*.CSV): No such file or directory.

Redirect argument from a file to a Linux command

I searched the Internet, but maybe I used the wrong keyword, but I couldn't find the syntax to my very simple problem below:
How do I redirect a file as command line arguments to the Linux command "touch"? I want to create a file with "touch abc.txt", but the filename should come from the filename "file.txt" which contains "abc.txt", not manually typed-in.
[root#machine ~]# touch < file.txt
touch: missing file operand
Try `touch --help' for more information.
[root#machine ~]# cat file.txt
abc.txt
Try
$ touch $(< file.txt)
to expand the content of file.txt and give it as argument to touch
Alternatively, if you have multiple filenames stored in a file, you could use xargs, e.g.,
xargs touch <file.txt
(It would work for just one, but is more flexible than a simple "echo").

Bash printf %q invalid directive

I want to change my PS1 in my .bashrc file.
I've found a script using printf with %q directive to escape characters :
#!/bin/bash
STR=$(printf "%q" "PS1=\u#\h:\w\$ ")
sed -i '/PS1/c\'"$STR" ~/.bashrc
The problem is that I get this error :
script.sh: 2: printf: %q: invalid directive
Any idea ? Maybe an other way to escape the characters ?
The printf command is built into bash. It's also an external command, typically installed in /usr/bin/printf. On most Linux systems, /usr/bin/printf is the GNU coreutils implementation.
Older releases of the GNU coreutils printf command do not support the %q format specifier; it was introduced in version 8.25, released 2016-10-20. bash's built-in printf command does -- and has as long as bash has had a built-in printf command.
The error message implies that you're running script.sh using something other than bash.
Since the #!/bin/bash line appears to be correct, you're probably doing one of the following:
sh script.sh
. script.sh
source script.sh
Instead, just execute it directly (after making sure it has execute permission, using chmod +x if needed):
./script.sh
Or you could just edit your .bashrc file manually. The script, if executed correctly, will add this line to your .bashrc:
PS1=\\u#\\h:\\w\$\
(The space at the end of that line is significant.) Or you can do it more simply like this:
PS1='\u#\h:\w\$ '
One problem with the script is that it will replace every line that mentions PS1. If you just set it once and otherwise don't refer to it, that's fine, but if you have something like:
if [ ... ] ; then
PS1=this
else
PS1=that
fi
then the script will thoroughly mess that up. It's just a bit too clever.
Keith Thompson has given good advice in his answer. But FWIW, you can force bash to use a builtin command by preceding the command name with builtin eg
builtin printf "%q" "PS1=\u#\h:\w\$ "
Conversely,
command printf "%s\n" some stuff
forces bash to use the external command (if it can find one).
command can be used to invoke commands on disk when a function with the same name exists. However, command does not invoke a command on disk in lieu of a Bash built-in with the same name, it only works to suppress invocation of a shell function. (Thanks to Rockallite for bringing this error to my attention).
It's possible to enable or disable specific bash builtins (maybe your .bashrc is doing that to printf). See help enable for details. And I guess I should mention that you can use
type printf
to find out what kind of entity (shell function, builtin, or external command) bash will run when you give it a naked printf. You can get a list of all commands with a given name by passing type the -a option, eg
type -a printf
You can use grep to see the lines in your .bashrc file that contain PS1:
grep 'PS1' ~/.bashrc
or
grep -n0 --color=auto 'PS1=' ~/.bashrc
which gives you line numbers and fancy coloured output. And then you can use the line number to force sed to just modify the line you want changed.
Eg, if grep tells you that the line you want to change is line 7, you can do
sed -i '7c\'"$STR" ~/.bashrc
to edit it. Or even better,
sed -i~ '7c\'"$STR" ~/.bashrc
which backs up the original version of the file in case you make a mistake.
When using sed -i I generally do a test run first without the -i so that the output goes to the shell, to let me see what the modifications do before I write them to the file.

What is EOF!! in the bash script?

There is a command I don't understand:
custom_command << EOF!!
I want to ask what EOF!! is in the bash script. I did find EOF with google, but google will ignore the "!!" automatically, so I cannot find EOF!!.
I know the end of the file token, but I don't exactly know what it means with the "!!" in the script. Is this a mark to force something to do something like in vim's wq! ?
Plus, why and when should we use EOF!! instead of EOF?
On the command line, !! would be expanded to the last command executed. Bash will print the line for you:
$ ls
a.txt b.txt
$ cat <<EOF!!
cat <<EOFls
>
In a script, though, history expansion is disabled by default, so the exclamation marks are part of the word.
#! /bin/bash
ls
cat <<EOF!!
echo 1
EOFls
echo 2
Produces:
a.txt b.txt
script.sh: line 7: warning: here-document at line 3 delimited by end-of-file (wanted `EOF!!')
echo 1
EOFls
echo 2
To enable history and history expansion in a script, add the following lines:
set -o history
set -H
You can use whatever string as here document terminator.
EOF!! is just what the person writing the script decided to use.
It's probably just a weird heredoc.
Example:
cat << EOF!!
blabla
EOF!!
Note: this only works in script files. The command line parser interprets !!.
As others already wrote, this is a here-document.
The token used for that should be chosen carefully; as the probability that the here-document contains EOF!! is lower than for EOF itself, they chose that.
I suppose they checked it does not harm before using it; !! in a script does NOT refer to the history, but it stays as it is.
The bash manual lists this under "Event designators", saying:
!!
Refer to the previous command. This is a synonym for !-1`.
I simply searched for "bash manual double exclamation".

Resources