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:]'
Related
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.
I am trying to disable nodes from apache loadbalancer using shell script. I got some idea online but I am not able to understand piece of code written for disabling the nodes form loadbalancer. Below is the code I am referring:
disable() {
balancer=$1
worker=$2
if [ -z "$balancer" ] || [ -z "$worker" ]; then
echo "Usage: $0 [-s host] [-p port] [-m balancer-manager] disable balancer_name worker_route"
echo " balancer_name : balancer/cluster name"
echo " worker_route : worker route e.g.)"
exit 1
fi
echo "Disabling $2 of $1..."
nonce=`$CURL -s "http://${server}:${port}/${manager}" | grep nonce | grep "${balancer}" | sed "s/.*nonce=\(.*\)['\"].*/\1/" | tail -n 1`
if [ -z "$nonce" ]; then
echo "balancer_name ($balancer) not found"
exit 1
fi
Can you please help me understand the meaning of above mentioned code. Especially, about nonce.
Picking apart your command, you probably first need to understand how pipes work in Unix. Each command has standard input and standard output; the notation first | second is a pipeline where the standard output from first becomes the standard input for second; so instead of first printing anything, or second reading something from a file, we pass whatever first would print as the input for second.
nonce=... assigns ... to the variable nonce. The name suggests this is intended to be used as a cryptographic nonce.
`cmd` is an obsolescent synonym for $(cmd). This command substitution replaces (substitutes) the command cmd with its output. So the value of nonce will be whatever this subshell prints to standard output.
$CURL is probably going to run curl, but we can only guess. Usually you would make sure curl is on your PATH and simply use the literal command curl.
curl -s http://whatever fetches the contents of the URL and prints them to standard output. The -s option suppresses any status messages. The output gets piped to ...
grep nonce, which prints to standard output any line which contains a match of the regular expression nonce (which simply matches this text verbatim anywhere on a line) and suppresses all others; which then gets piped to ...
grep "${balancer}" which similarly prints only lines which match whatever regular expression the variable balancer contains (the braces are harmless but useless); which then gets piped to ...
sed "s/.*nonce=\(.*\)['\"].*/\1/" which picks out the stuff between the parentheses -- anything up to the last single or double quote after nonce=; which then gets piped to ...
tail -n 1 which discards all lines except the last one.
So in summary, pick out the last occurrence of nonce= from the content behind the remote URL, and print that, but only up to just before the first single or double quote.
This is all rather clumsy and inefficient; any pipe involving multiple grep and sed commands should probably be refactored to a simple Awk script.
nonce=$(curl -s "http://${server}:${port}/${manager}" |
awk -v b="$balancer" '/nonce/ && $0 ~ b {
sub(/^.*nonce=/, ""); sub(/[\047\042][^\047\042]*$/, ""); n=$0 }
END { print n }')
The sed command in particular looks slightly obscure; normally, we would expect the output we want to extract to be between quotes, but this extracts up to just before the last single or double quote. The command sed -n 's/.*\(stuff\).*/\1/p' would be the normal way to only print the lines from which we actually managed to extract stuff. But without seeing what the URL contains, we can only speculate about whether this is correct or not. Certainly the conventional syntax would have allowed the author to omit the first grep entirely.
Getting the last nonce is probably just a guardrail to make sure there is never more than one; I would assume we would normally expect only a single match.
grep, sed, and Awk all operate on regular expressions. If you are new to regex, perhaps visit the Stack Overflow regex tag info page and check out the list of learning resources near the end.
Going forward, probably try https://explainshell.com/ before asking for human assistance.
This question already has answers here:
Why does a space in a variable assignment give an error in Bash? [duplicate]
(3 answers)
How do I set a variable to the output of a command in Bash?
(15 answers)
Closed 3 years ago.
I'm new to bash script, it is interesting, but somehow I'm struggling with everything.
I have a file, separated by tab "\t" with 2 infos : a string and a number.
I'd like to use both info on each line into a bash script in order to look into a file for those infos.
I'm not even there, I'm struggling to give the arguments from the two columns as two arguments for bash.
#/!bin/bash
FILE="${1}"
while read -r line
do
READ_ID_WH= "echo ${line} | cut -f 1"
POS_HOTSPOT= echo '${line} | cut -f 2'
echo "read id is : ${READ_ID_WH} with position ${POS_HOTSPOT}"
done < ${FILE}
and my file is :
ABCD\t1120
ABC\t1121
I'm launching my command with
./script.sh file_of_data.tsv
What I finally get is :
script.sh: line 8: echo ABCD 1120 | cut -f 1: command not found
I tried a lot of possibilities by browsing SO, and I can't make it to divide my line into two arguments to be used separately in my script :(
Hope you can help me :)
Best,
The quotes cause the shell to look for a command whose name is the string between the quotes.
Apparently you are looking for
while IFS=$'\t' read -r id hotspot; do
echo "read id is: $id with position $hotspot"
done <"$1"
You generally want to avoid capturing things into variables you only use once, but the syntax for that is
id=$(echo "$line" | cut -f1)
See also Correct Bash and shell script variable capitalization and When to wrap quotes around a shell variable?. You can never have whitespace on either side of the = assignment operator (or rather, incorrect whitespace changes the semantics to something you probably don't want).
You have a space after the equals sign on lines 5 and 6, so it thinks you are looking for an executable file named echo ABCD 1120 | cut -f 1 and asking to execute it while assigning the variable READ_ID_WH to the empty string, rather than assigning the string you want to the variable.
I'm currently trying to build a shell script that sends broadcast UDP packets. My problem is that my echo is outputting the arguments instead, and I have no ideia why. Here's my script:
#!/bin/bash
# Script
var1="\xdd\x02\x00\x13\x00\x00\x00\x10\x46\x44\x30\x30\x37\x33\x45\x31\x39\x39\x45\x43\x31\x42\x39\x34\x00"
var2="\xdd\x00\x0a\x\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x02"
echo -ne $var1 | socat - UDP4-DATAGRAM:255.255.255.255:5050,broadcast
echo -ne $var2 | socat - UDP4-DATAGRAM:255.255.255.255:5050,broadcast
Using wireshark I can see the script is printing -ne as characters and also is not converting each \xHH to the correspondant ASCII character.
Thanks!
I figured my problem out. It turns out I was runninc my script with sh ./script.sh instead of bash ./script.sh
echo implementations are hopelessly inconsistent about whether they take command options (like -ne) or simply treat them as part of the string to print, and/or whether they interpret escape sequences in the strings to print. It sounds like you're seeing a difference between bash's builtin version of echo vs. (I'm guessing) the version in /bin/echo. I've also seen it vary even between different versions of bash.
If you want consistent behavior for anything nontrivial, use printf instead of echo. It's slightly more complicated to use it correctly, but IMO worth it because your scripts won't randomly break because echo changed for whatever reason. The tricky thing about printf is that the first argument is special -- it's a format string in which all escape sequences are interpreted, and any % sequences tell it how to add in the rest of the arguments. Also, it doesn't add a linefeed at the end unless you specifically tell it to. In this case, you can just give it the hex codes in the format string:
printf "$var1" | socat - UDP4-DATAGRAM:255.255.255.255:5050,broadcast
printf "$var2" | socat - UDP4-DATAGRAM:255.255.255.255:5050,broadcast
I'm working with Mac OS X's pbpaste command, which returns the clipboard's contents. I'd like to create a shell script that executes each line returned by pbpaste as a separate bash command. For example, let's say that the clipboard's contents consists of the following lines of text:
echo 1234 >~/a.txt
echo 5678 >~/b.txt
I would like a shell script that executes each of those lines, creating the two files a.txt and b.txt in my home folder. After a fair amount of searching and trial and error, I've gotten to the point where I'm able to assign individual lines of text to a variable in a while loop with the following construct:
pbpaste | egrep -o [^$]+ | while read l; do echo $l; done
which sends the following to standard out, as expected:
echo 1234 >~/a.txt
echo 5678 >~/b.txt
Instead of simply echoing each line of text, I then try to execute them with the following construct:
pbpaste | egrep -o [^$]+ | while read l; do $l; done
I thought that this would execute each line (thus creating two text files a.txt and b.txt in my home folder). Instead, the first term (echo) seems to be interpreted as the command, and the remaining terms (nnnn >~/...) seem to get lumped together as if they were a single parameter, resulting in the following being sent to standard out without any files being created:
1234 >~/a.txt
5678 >~/b.txt
I would be grateful for any help in understanding why my construct isn't working and what changes might get it to work.
[…] the remaining terms (nnnn >~/...) seem to get lumped together as if they were a single parameter, […]
Not exactly. The line actually gets split on whitespace (or whatever $IFS specifies), but the problem is that the redirection operator > cannot be taken from a shell variable. For example, this snippet:
gt='>'
echo $gt foo.txt
will print > foo.txt, rather than printing a newline to foo.txt.
And you'll have similar problems with various other shell metacharacters, such as quotation marks.
What you need is the eval builtin, which takes a string, parses it as a shell command, and runs it:
pbpaste | egrep -o [^$]+ | while IFS= read -r LINE; do eval "$LINE"; done
(The IFS= and -r and the double-quotes around $LINE are all to prevent any other processing besides the processing performed by eval, so that e.g. whitespace inside quotation marks will be preserved.)
Another possibility, depending on the details of what you need, is simply to pipe the commands into a new instance of Bash:
pbpaste | egrep -o [^$]+ | bash
Edited to add: For that matter, it occurs to me that you can pass everything to eval in a single batch; just as you can (per your comment) write pbpaste | bash, you can also write eval "$(pbpaste)". That will support multiline while-loops and so on, while still running in the current shell (useful if you want it to be able to reference shell parameters, to set environment variables, etc., etc.).