What is EOF!! in the bash script? - linux

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".

Related

How can I give special command (insert) to vi?

I remember doing magic with vi by "programming" it with input commands but I don't remember exactly how.
My sepcial request are:
launch vi in a script with command to be executed.
do an insert in one file.
search for a string in the file.
use $VARIABLE in the vi command line to replace something in the command.
finish with :wq.
I my memory, I sent the command exactly like in vi and the ESC key was emulate by '[' or something near.
I used this command into script to edit and change files.
I'm going to see the -c option but for now I cannot use $VARIABLE and insert was impossible (with 'i' or 'o').
#!/bin/sh
#
cp ../data/* .
# Retrieve filename.
MODNAME=$(pwd | awk -F'-' '{ print $2 }')
mv minimod.c $MODNAME.c
# Edit and change filename into the file (from mimimod to the content of $VARIABLE).
vi $MODENAME.c -c ":1,$s/minimod/$MODNAME/" -c ':wq!'
This example is not functionning (it seems the $VARIABLE is not good in -c command).
Could you help me remember memory ;) ?
Thanks a lot.
Joe.
You should not use vi for non-interactive editing. There's already another tool for that, it's called sed or stream editor.
What you want to do is
sed -i 's/minimod/'$MODNAME'/g' $MODNAME.c
to replace your vi command.
Maybe you are looking for the ex command, that can be run non-interatively:
ex $MODENAME.c <<< "1,\$s/minimod/$MODNAME/
wq!
"
Or if you use an old shell that does not implement here strings:
ex $MODENAME.c << EOF
1,\$s/minimod/$MODNAME/
wq!
EOF
Or if you do not want to use here documents either:
echo "1,\$s/minimod/$MODNAME/" > cmds.txt
echo "wq!" >> cmds.txt
ex $MODENAME.c < cmds.txt
rm cmds.txt
One command per line in the standard input. Just remember not to write the leading :. Take a look at this page for a quick review of ex commands.
Granted, it would be better to use the proper tool for the job, that would be sed, as #IsaA answer suggests, or awk for more complex commands.

is there any bash symbol to represent redirection operand in Linux interactive use?

I am looking for a shell parameter to represent the redirection operand.
e.g: data/temp.txt in this command:
cat file.txt > data/temp.txt
Is there any such bash special Parameters which will allow me to open the file that I am redirecting to in interactive use, after the command will exit ?
$ cat file.txt > data/temp.txt
$ vim data/temp.txt
I don't think so. But since the redirection operand usually is the last, you can use Bash's history expansion:
echo hello > testfile
cat !$ # cat testfile
Or for interactive session, alt+. or esc+_ can be used to insert the last word of the previous line, as a comment pointed out.
Bash documentation for history expansion.
Another possibility here is bash's process substitution -- if you're most interested in opening an editor on the output of cat, but you're not as interested in writing the output to a file, you can use
vim <(cat file.txt)
This writes stdout to a named pipe and opens the named pipe (probably named something like /dev/fd/NN) in a text editor. You make your edits, and you can then save your edits using <esc>:w data/temp.txt if you wish.

backticks `cat filename` failed, file content is several commands

My system is CentOS 6.5
When I want to use backtick to run the commands in filename, i got the result below:
the file's content is below:
[liu-uil#~ 15:54:16]$cat test
echo 1;
echo 2;
echo 3;
[liu-uil#~ 15:54:18]$`cat test`
1; echo 2; echo 3;
[liu-uil#~ 15:54:24]$
the commands after the first echo are all treated as text plain, I don't know why? Could somebody kindly explain this to me? Thank you very much!
Command substitution is one of the expansions. Expansions happen when the command line was already split into commands, it's too late to create new commands.
You can use
eval `cat 1`
to call the shell parser again to split the string into commands and run them.
Only the first word in the result of a backtick command is interpreted as a command. Remaining text is the argument list.
If you want to run commands in a file, you don't need backticks, you need the dot (.) command:
[liu-uil#~ 15:54:16]$. test

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.

Why doesn't this bash code work?

x="a=b"
`echo $x`
echo $a
I expect the second line to generate "a=b", and execute it in the context of the main shell, resulting in a new variable a with value b.
However, what I really get (if I enter the commands manually) is the error message after the second line, bash: a=b: command not found
Why is that so?
Try
eval $x
(And we need 30 characters for this answer to be posted)
What your first echo line does is running in a subshell and returns its value to the callee.. The same result is achieved using $() and is - by the way - easier to use than backticks.
So, what you are doing is first running echo $x (which returns a=b). And, because of the backticks, a=b is returned to the shell that tries to run that line as a command which - obviously - won't work.
Try this in a shell:
$(echo ls)
And you will clearly see what is happening.
It's because of the order in which bash parses the command line. It looks for variable definitions (e.g. a=b) before performing variable and command substitution (e.g. commands in backticks). Because of this, by the time echo $x is replaced by a=b, it's too late for bash to see this as a variable definition and it's parsed as a command instead. The same thing would've happened if you'd just used $x as the command (instead of echo in backticks). As in #mvds's answer, the eval command can be used to force the command to be reparsed from the beginning, meaning that it'll be recognized as a variable definition:
$ x="a=b"
$ `echo $x`
-bash: a=b: command not found
$ $(echo $x) # Exact same thing, but with cleaner syntax
-bash: a=b: command not found
$ $x # This also does the same thing, but without some extra steps
-bash: a=b: command not found
$ eval "$x" # This will actually work
$ echo $a
b
$ a= # Start over
$ eval "$(echo "$x")" # Another way of doing the same thing, with extra steps
$ echo $a
b
Note that when using eval I've put all of the references to $x in double-quotes -- this is to prevent the later phases of bash parsing (e.g. word splitting) from happening twice, since bash will finish its regular parsing process, then recognize the eval command, and then redo the entire parsing process again. It's really easy to get unexpected results from using eval, and this removes at least some of the potential for trouble.
Did you try $x in that funny apostrophes? Without echo, echo seems to be only for displaying string, not execute commands.

Resources