Understanding cat << ! syntax [duplicate] - bash

This question already has answers here:
How does "cat << EOF" work in bash?
(12 answers)
Closed 6 years ago.
I'm new to Bash scripting. I came across this link and noticed an unusual syntax for cat.
cat << !
HOPE THIS WORKS
This sample E-mail message demonstrates how one can attach
files when sending messages with the Unix sendmail utility.
!
uuencode ${file_1} ${file_1}
uuencode ${file_2} ${file_2}
uuencode ${file_3} ${file_3}
!
What does the << mean? What does the ! mean? How come the cat has an opening and closing !, but uuencode doesn't?
EDIT: Thanks for all the help! The last outstanding question I have is why is there no opening and marker for the uuencode section. From what I'm understanding, cat has a << ! indicating it's a HEREDOC. uuencode however doesn't seem like a HEREDOC. What gives?

Its called heredoc, and its constructed like this:
command << MARKER
Literal text here, can contain $variables
and newlines,
leading spaces
and tabs...
MARKER
And works with everything between the MARKER is going as stdin to the specified command. What marker you use is optional and in your example ! is being used. And if you feed the command cat with input on stdin it will simply print it to stdout:
cat << !
all this text will go to stdout
!

Related

how to run bash for each line in command output [duplicate]

This question already has answers here:
How can I loop over the output of a shell command?
(4 answers)
Closed 2 months ago.
i am building a bash script that is supposed to put each line of the output of one command to an variable and then run some commands on that, i am doing it like
for i in `cmd`
do
echo i=$i
lang=$(echo $i | cut -d '"' -f 6)
echo lang=$lang
#some stuff
done
my problem is that for is using space and newlines for separation to different $i's and i want it to do create only new $i's with newline delimiters cause every line may have a cupple of spaces and i want them no matter that handled as it own...
google but found nothing really helping me, only suggestions to use xargs which dosnt help me cause i need to use not one command but a cupple after creating some variables and running some if statements that desiside which command is to run if any...
If you want to read cmd's output line by line you can do it using
while loop and bash's internal read command
cmd | while IFS= read -r i
do
echo "i=${i}"
lang="$(echo "${i}" | cut -d '"' -f 6)"
echo "lang=${lang}"
#some stuff
done
Use " around a variable's de-reference to avoid problems with spaces inside it's value.

How to read the last characters not ending with a newline from a stream [duplicate]

This question already has answers here:
Reading input files by line using read command in shell scripting skips last line
(5 answers)
Shell script read missing last line
(7 answers)
Closed 4 years ago.
I have a script called test.sh which processes the standard input line by line like this:
#!/usr/bin/env bash
echo "start"
while IFS= read -r line; do
echo "processing[$line]"
done < /dev/stdin
echo "done"
The problem with this is, it doesn't process the characters between the last newline and the eof.
printf $'line 1\nline 2\nlast chars' | test.sh
will output
start
processing[line 1]
processing[line 2]
done
The reason I read line by line is that I need to inspect the first line and in some cases I want to remove it from the output stream.
How can I process these last characters? I've looked into read -n but then I would need to supply how many characters to expect at a maximum and I rather don't build in limits.
Also: I wouldn't know where to put this statement in the while-loop. I'm on the macOS platform.

Why "echo <<EOF" not working as expected [duplicate]

This question already has answers here:
Bash here document produces no output, any idea why?
(2 answers)
Closed 5 years ago.
I am trying to understand the bash's here document feature. Below code works as expected and returns "abc" to terminal. If I replace the program cat by echo I do not see any output. Why I am not able to pass here document to echo ? Is it becuase it is a bash builtin ?
cat <<EOF
abc
EOF
"abc" is output to the terminal as expected.
No output for below comamnd though-
echo <<EOF
abc
EOF
You want:
cat <<EOF
abc
EOF
Otherwise, what you're doing is just running echo with its stdin connected to a temporary file having abc in it. Since echo doesn't read stdin, it never finds out if there's contents waiting to be read from there or not.

Remove punctuation standard input. [duplicate]

This question already has answers here:
echo "#!" fails -- "event not found"
(5 answers)
Closed 7 years ago.
I want the program can remove punctuation which read from the standard input
My code is:
echo $* | tr -d '[:punct:]'
It can handle some simple situations but when I type input in terminal (like: whatever ad!":)
when the sentence within continuing several punctuation, the terminal will reflect the result: -bash: !": event not found
Can anyone give help?
single quotes should be used to avoid expansion, e.g.
echo 'whatever ad!' | tr -d '[:punct:]'
under a bash shell it prints out
whatever ad
and if you want to use a variable
BUFF='whatever ?_-!!!!ad!'; echo "$BUFF" | tr -d '[:punct:]'
EDIT 1
this is a complete script following your request
#!/bin/sh
functionStripAndPrint()
{
echo "$#" | tr -d '[:punct:]'
}
functionStripAndPrint "$#"
assuming that this script is stored in the stripchars.sh file, you can invoke it like so
./stripchars.sh 'das !adsa _sda ssad-'
and it will print
das adsa sda ssad
EDIT 2
you can work around the interpretation of some of the special characters with set, for example
set +H
deactivates the H option which is linked to the ! symbol, so now ! is just an exclamation mark with no special meaning. You can then simplify your invocation a little bit
./stripchars.sh sdfsa!fdsaf?\'
as you can see the only problem at this point is the ' that still needs to be escaped.
If you want to re-enable the H you do
set -H
set is handy to modify the behaviour of your shell, I don't know if it's worth in your case, the shell is good and handy for some basic stuff, but I don't know if this will fit your needs, you know better, take a look at set and see if it's enough.
As you probably know, bash uses ! to get commands from the history of commands. When you type
echo Whatever ad!":
it tries to retrieve the command from its command history by using !":. Since it does not find any command using that, it prints the message
bash: !": event not found
You can pass those special characters to bash by (1) using single quote to let special characters be treated like normal characters, or (2) escaping the special characters.
echo 'Whatever ad!":' | tr -d '[:punct:]'
echo Whatever ad\!\": | tr -d '[:punct:]'

Treat arguments as input stream in bash

Is there any bash trick that allows giving some parameters in command line to a program that gets its inputs via input stream? Something like this:
program < 'a=1;b=a*2;'
but < needs a file input stream.
For very short here-documents, there are also here-strings:
program <<< "a=1;b=a*2"
I think
echo 'a=1;b=a*2;' | program
is what you need. This process is called "piping"
As a side note: doing the opposite (i.e. piping other programs output as arguments) could be done with xargs
echo works great. The other answer is Here-documents [1]
program <<EOF
a=1;b=a*2;
EOF
I use echo when I have one very short thing on one line, and heredocs when I have something that requires newlines.
[1] http://tldp.org/LDP/abs/html/here-docs.html
shopt -s expand_aliases
alias 'xscript:'='<<:ends'
xscript: bc | anotherprog | yetanotherprog ...
a=1;b=a*2;
:ends
Took me a year to hack this one out. Premium bash script here fellas. Give respect where due please :)
I call this little 'diddy' xscript because you can expand bash variables and substitutions inside of the here document.
alias 'script:'='<<":ends"'
The above version does not expand substitutions.
xscript: cat
The files in our path are: `ls -A`
:ends
script: cat
The files in our path are: `ls -A`
:ends
I'm not finished!
source <(xscript: cat
echo \$BASH "hello world, I'mma script genius!"
echo You can thank me now $USER
:ends
)

Resources