tilde based exec from a variable - bash

This sure is something simple but I can't seem to figure out. Any idea what's wrong when I exec the same command via a variable?
this works:
$ echo `git --version | cut -d' ' -f3`
1.8.4
but this doesn't:
$ c="git --version | cut -d' ' -f3"
$ echo `$c`
git version 1.8.4
Any ideas?

Your code:
c="git --version | cut -d' ' -f3"
echo `$c`
Is the equivalent of:
git --version "|" cut -d' ' -f3
Since the pipe character is inside a variable, it will not get evaluated as a shell metacharacter, and its the same effect as if you typed it on the command line with quotes or backslash to escape it. This is easier to see if you use this instead:
c="echo a b c | wc -w"
echo `$c`
As already posted, the answer is to use "eval" so the shell expands the variable and then tries to execute it as shell source, then metacharacters like the pipe will be recognized.

I don't have the exact reason at my fingertips, but ... you can't do that.
Pipes and redirections seem to be especially special.
However, eval will do the job:
$ c="git --version | cut -d' ' -f3"
$ eval "$c"
1.7.5.4

Related

awk shell variables not working

Hi I'm using GNU awk version 3.1.5 and I've specified 2 variables in a KSH script
PKNAME= ls -lt /var/db/pkg | tr -s " " | cut -d" " -f9
PKDATE= ls -lt /var/db/pkg/$PKNAME/ | tr -s " " | cut -d" " -f6-8
I'm trying to prove that I'm getting the correct output, by running a test using
echo bar
awk -F, -v pkname="$PKNAME" -v pkdate="$PKDATE" 'BEGIN{print pkname, pkdate, "foo"; exit}'
echo baz
The output from this results in 2 blank spaces and foo, like so
bar
foo
baz
I have tried, double quoting the variables, single quotes and back ticks. Also tried double quotes with back ticks.
Any ideas why the variables are not being executed? I'm fairly new to awk and appreciate any help! Thanks
I suppose it is possible that it is not possible to run a sub shell comand within an awk statement. Is this true?
This has nothing to do with awk. The problem is in the way you're assigning your variables. Your lines should be like this:
PKNAME=$(ls -lt /var/db/pkg | tr -s " " | cut -d" " -f9)
There can be no spaces around either side of an assignment in the shell.
At the moment, you're running the command ls -lt ... with a variable PKNAME temporarily assigned to an empty string. In subsequent commands the variable remains unset.
Your awk command should remain unchanged, i.e. the shell variables should be passed like -v pkname="$PKNAME". As an aside, it's generally considered bad practice to use uppercase variable names, as these should be reserved for internal use by the shell.

Bash code error unexpected syntax error

I am not sure why i am getting the unexpected syntax '( err
#!/bin/bash
DirBogoDict=$1
BogoFilter=/home/nikhilkulkarni/Downloads/bogofilter-1.2.4/src/bogofilter
echo "spam.."
for i in 'cat full/index |fgrep spam |awk -F"/" '{if(NR>1000)print$2"/"$3}'|head -500'
do
cat $i |$BogoFilter -d $DirBogoDict -M -k 1024 -v
done
echo "ham.."
for i in 'cat full/index | fgrep ham | awk -F"/" '{if(NR>1000)print$2"/"$3}'|head -500'
do
cat $i |$BogoFilter -d $DirBogoDict -M -k 1024 -v
done
Error:
./score.bash: line 7: syntax error near unexpected token `('
./score.bash: line 7: `for i in 'cat full/index |fgrep spam |awk -F"/" '{if(NR>1000)print$2"/"$3}'|head -500''
Uh, because you have massive syntax errors.
The immediate problem is that you have an unpaired single quote before the cat which exposes the Awk script to the shell, which of course cannot parse it as shell script code.
Presumably you want to use backticks instead of single quotes, although you should actually not read input with for.
With a fair bit of refactoring, you might want something like
for type in spam ham; do
awk -F"/" -v type="$type" '$0 ~ type && NR>1000 && i++<500 {
print $2"/"$3 }' full/index |
xargs $BogoFilter -d $DirBogoDict -M -k 1024 -v
done
This refactors the useless cat | grep | awk | head into a single Awk script, and avoids the silly loop over each output line. I assume bogofilter can read file name arguments; if not, you will need to refactor the xargs slightly. If you can pipe all the files in one go, try
... xargs cat | $BogoFilter -d $DirBogoDict -M -k 1024 -v
or if you really need to pass in one at a time, maybe
... xargs sh -c 'for f; do $BogoFilter -d $DirBogoDict -M -k 1024 -v <"$f"; done' _
... in which case you will need to export the variables BogoFilter and DirBogoDict to expose them to the subshell (or just inline them -- why do you need them to be variables in the first place? Putting command names in variables is particularly weird; just update your PATH and then simply use the command's name).
In general, if you find yourself typing the same commands more than once, you should think about how to avoid that. This is called the DRY principle.
The syntax error is due to bad quoting. The expression whose output you want to loop over should be in command substitution syntax ($(...) or backticks), not single quotes.

"not found" error in shell script

I am trying to write a script that should take values from a xml file.
Here is the xml file :-
`<manifestFile>
<productInformation>
<publicationInfo>
<pubID pcsi-selector="P.S.">PACODE</pubID>
<pubNumber/>
</publicationInfo>
</productInformation>
</manifestFile>`
and i my code is
:-
#!/bin/sh
Manifest=""
Manifest= `/bin/grep 'pcsi-selector="' /LDCManifest.xml | cut -f 2 -d '"'`
echo $Manifest
I expect my result to be P.S. , but it keeps throwing error as :-
./abc.sh: P.S.: not found
I am new to shell and i am not able to figure out whats the error here ?
You can't have a space after the =.
When you run this command:
Manifest= `/bin/grep 'pcsi-selector="' /LDCManifest.xml | cut -f 2 -d '"'`
It's the same as this:
Manifest='' `/bin/grep 'pcsi-selector="' /LDCManifest.xml | cut -f 2 -d '"'`
That tells the shell to
Run the grep command.
Take its output
Run that output as a command, with the environment variable Manifest set to the empty string for the duration of the command.
Get rid of the space after the = and you'll get the result you want.
However, you should also avoid using backticks for command substitution, because they interfere with quoting. Use $(...) instead:
Manifest=$(grep 'pcsi-selector="' /LDCManifest.xml | cut -f2 -d'"')
Also, using text/regex-based tools like grep and cut to manipulate XML is clunky and error-prone. You'd be better off installing something like XMLStarlet:
Manifest=$(xmlstarlet sel -t \
-v '/manifestFile/productInformation/publicationInfo/pubID/#pcsiselector' -n \
/LDCManifest.xml)
Or simpler:
grep -oP 'pcsi-selector="\K[^"]+' /LDCManifest.xml
would print
P.S.
assign
Manifest=$(grep -oP 'pcsi-selector="\K[^"]+' /LDCManifest.xml)

Passing Bourne Shell variable into cut command

I am trying to do the following.
foo="foo:foo1"
cc= `$foo | cut -f2 -d:`
I understand why this would not work but I am at a loss as to do this.
Thanks in advance.
Try this:
foo="foo:foo1"
cc=`echo $foo | cut -f2 -d:`
There are 2 changes to make:
You need to echo the value of shell
variable foo and then cut it.
You must not have white spaces around =
when assigning a value to a shell
variable.
in bourne, you can use set. No external command needed.
$ foo="foo:foo1"
$ IFS=":"
$ set -- $foo
$ echo $2
foo1

How to extract and chop version string from file in bash

I have a simple text file ./version containing a version number. Unfortunately, sometimes the version number in the file is followed by whitespaces and newlines like
1.1.3[space][space][newline]
[newline]
[newline]
What is the best, easiest and shortest way to extract the version number into a bash variable without the trailing spaces and newlines? I tried
var=`cat ./version | tr -d ' '`
which works for the whitespaces but when appending a tr -d '\n' it does not work.
Thanks,
Chris
$ echo -e "1.1.1 \n\n" > ./version
$ read var < ./version
$ echo -n "$var" | od -a
0000000 1 . 1 . 1
0000005
Pure Bash, no other process:
echo -e "1.2.3 \n\n" > .version
version=$(<.version)
version=${version// /}
echo "'$version'"
result: '1.2.3'
I still do not know why, but after deleting and recreating the version file this worked:
var=`cat ./version | tr -d ' ' | tr -d '\n'`
I'm confused... what can you do different when creating a text file. However, it works now.
I like the pure bash version from fgm's answer.
I provide this one-line perl command to remove also other characters if any:
perl -pe '($_)=/([0-9]+([.][0-9]+)+)/'
The extracted version number is trimmed/stripped (no newline or carriage return symbols):
$> V=$( bash --version | perl -pe '($_)=/([0-9]+([.][0-9]+)+)/' )
$> echo "The bash version is '$V'"
The bash version is '4.2.45'
I provide more explanation and give other more sophisticated (but still short) one-line perl commands in my other answer.

Resources