Unfamiliar shell syntax in ack-grep install script - bash

From the ack installation page (http://betterthangrep.com/install/) there is a one-liner installation with curl:
curl http://betterthangrep.com/ack-standalone > ~/bin/ack && chmod 0755 !#:3
I understand that it's getting the file from the website and saving it to ~/bin/ack, then setting permissions, but what does that last part ( !#:3 ) do ? (I do not recognize the syntax and Googling didn't yield any helpful results)

See the section called HISTORY EXPANSION in man bash, particularly the Word Designators subsection. !#:3 refers to the third word of the pipe, which is (in your example) ~/bin/ack. In order, the words of the command are curl, 0; http://betterthangrep.com/ack-standalone, 1; >, 2; ~/bin/ack, 3; &&, 4; chmod, 5; 0755, 6; !#:3, 7.
That is, !#:3 is a way to repeat the filename without using a separate variable or literal text.
Regarding the question about > and whitespace, note that > is a metacharacter, which man bash defines as a “character that, when unquoted, separates words. One of the following: | & ; ( ) < > space tab”. So whitespace does not affect whether > counts as a token. But note that in the following example, the first 3 is quoted so that bash doesn't interpret it as part of a 3> redirection. When the line was entered, bash echoed the expanded line and then executed it.
$ seq '3'>bbb;cat !#:3 !#:2 ccc; head !#:3 !#:8
seq '3'>bbb;cat bbb > ccc; head bbb ccc
==> bbb <==
1
2
3
==> ccc <==
1
2
3

!# means to execute the command typed so far, but you can specify a parameter with :n. :0 would be the first word (curl), :1 the second one (http...) and so on.

Related

Replace line containing variables and space by bash commands

I have some files containing lines, some of them are similar that shown below:
HETATM 2340 C2 2FN 1 15.566 27.839 11.677 1.00 24.33 C
I need to replace
2FN 1
to
2FN D 1
so that the final result is:
HETATM 2340 C2 2FN 1 15.566 27.839 11.677 1.00 24.33 C
This is rather easy by using sed command and in the case of you always have the same words to replace
sed 's/2FN 1/2FN D 1/g' input.file > output.file
However, in the case one wants to use variables
A="2FN"
B="1"
in sed command, the result is not what is expected, I suppose due to the multiple spaces in the text to replace.
I tried several ways, such as:
A="2FN"
B="1"
S=' '
G=$(echo "$LIG${S}$LIGN")
sed 's/$G/2FN D 1/g' input.file > output.file
But no expected result has been obtained.
Interestingly, by echo G variable is:
"2FN 1"
but sed doesn't replace to
"2FN D 1"
Do you have any suggestions?
Thanks
The problem is you're trying to get bash to resolve variables within single quotes. Single quotes are telling bash: "Don't resolve anything in here, take it literally as is"
If you simply replace the single quotes in your sed command with double quotes, as #oguzismail suggested, you'll be fine.
Much more detail, if needed, is here:
https://stackoverflow.com/a/13802438/236528

Unix FOR loop not working with IP addressees

The below loop should read 1 line at a time to the veriable m. But it prints some junk value. Please help.
MyMachine:/u/home/Mohammed_Junaid> cat /tmp/F5
[10.222.73.99:22]
[10.000.73.99:22]
[10.111.73.99:22]
MyMachine:/u/home/Mohammed_Junaid>
MyMachine:/u/home/Mohammed_Junaid> for m in $(cat /tmp/F5); do echo $m;done
1
2
1
2
1
2
MyMachine:/u/home/Mohammed_Junaid>
Those strings are also valid glob-patterns. Because you're not quoting the $m variable, you're letting the shell perform filename expansion.
It happens that the string [10.222.73.99:22] is equivalent to the glob pattern [012739.:] which will match a filename of a single one of those characters, and it appears that you have a file named 1 and a file named 2 in your current directory.
Always quote your shell variables, and don't use for to iterate the lines of a file
while IFS= read -r m; do echo "$m"; done < /tmp/F5

bash how to input * from a file

The problem:
I have a simple compiler that compiles simple RPN expression. The compiler is invoked like this:
./compiler 1 2 3 + "*"
This works fine.
Now, let's say I've put
1 2 3 + "*"
into a file called input. Then when I invoke the compiler like this:
./compiler $(cat input)
My compiler will complain: unknown symbol: "*"
If I remove the double quote around *, the * gets expanded to file names. I've also tried '' and ``, no good.
So, how can I input a normal * from a file?
zoo.sh with content
#!/bin/bash
set -f
echo $#
m.txt with content:
1 2 3 + *
In a shell do:
set -f
./zoo.sh 1 2 4 + *
1 2 4 + *
./zoo.sh $(cat m.txt)
1 2 3 + *
The shell is doing the expansion for you. This happens before the command runs. If you want it to stop you need to explicitly tell it. Read about it here:
http://www.gnu.org/software/bash/manual/bash.html#The-Set-Builtin
Above the script also sets this to prevent the echo inside the script to do the expansion and make this work as I imagine your code works. Your compiler probably does not (need) do this.
Remember to do a set +f to restore the filename expansion.
The only way to remove quoting characters from a variable that contains them:
a='1 2 3 + "*"'
is with shell "quote removal". That is done every time a string is parsed by the shell (if it is not the result of some other expansion). So, this will not remove the quotes, even if un-quoted:
$ echo $a
1 2 3 + "*"
Even repeated, quotes will not be removed:
$ echo $(echo $( echo $a ) )
1 2 3 + "*"
But quotes will be removed if we re-parse the string with eval:
$ eval echo $a
1 2 3 + *
Or if the string is sent to an external program (not a builtin), it will be re-parsed on its return. That is what happens with xargs:
$ xargs <<<$a
1 2 3 + *
Now, for your specific case, either do:
$ eval echo $(<file.csv)
or
$ xargs <file.csv
In this case, xargs is executing a default echo, so is basically doing the same as above.
Please be aware that either commands work with this specific code, but are a source for serious security risks. Remember: eval is evil.

bash: calling a scripts with double-quote argument

I have a bash scripts which an argument enclosed with double quotes, which creates a shape-file of map within the given boundries, e.g.
$ export_map "0 0 100 100"
Within the script, there are two select statements:
select ENCODING in UTF8 WIN1252 WIN1255 ISO-8859-8;
...
select NAV_SELECT in Included Excluded;
Naturally, these two statements require the input to enter a number as an input. This can by bypassed by piping the numbers, followed by a newline, to the script.
In order to save time, I would like to have a script that would create 8 maps - for each combination of ENCODING (4 options) and NAV_SELECT (2 options).
I have written another bash script, create_map, to server as a wrapper:
#!/bin/bash
for nav in 1 2 3 4;
do
for enc in 1 2;
do
printf "$nav\n$enc\n" | /bin/bash -c "./export_map.sh \"0 0 100 100\""
done
done
**This works (thanks, Brian!), but I can't find a way to have the numeric argument "0 0 100 100" being passed from outside the outer script. **
Basically, I'm looking for way to accept an argument within double quotes to a wrapper bash script, and pass it - with the double quotes - to an inner script.
CLARIFICATIONS:
export_map is the main script, being called from create_map 8 times.
Any ideas?
Thanks,
Adam
If I understand your problem correctly (which I'm not sure about; see my comment), you should probably add another \n to your printf; printf does not add a trailing newline by default the way that echo does. This will ensure that the second value will be read properly by the select command which I'm assuming appears in export_map.sh.
printf "$nav\n$enc\n" | /bin/bash -c "./export_map.sh \"100 200 300 400\""
Also, I don't think that you need to add the /bin/bash -c and quote marks. The following should be sufficient, unless I'm missing something:
printf "$nav\n$enc\n" | ./export_map.sh "100 200 300 400"
edit Thanks for the clarification. In order to pass an argument from your wrapper script, into the inner script, keeping it as a single argument, you can pass in "$1", where the quotes indicate that you want to keep this grouped as one argument, and $1 is the first parameter to your wrapper script. If you want to pass all parameters from your outer script in to your inner script, each being kept as a single parameter, you can use "$#" instead.
#!/bin/bash
for nav in 1 2 3 4;
do
for enc in 1 2;
do
printf "$nav\n$enc\n" | ./export_map.sh "$1"
done
done
Here's a quick example of how "$#" works. First, inner.bash:
#!/bin/bash
for str in "$#"
do
echo $str
done
outer.bash:
#!/bin/bash
./inner.bash "$#"
And invoking it:
$ ./outer.bash "foo bar" baz "quux zot"
foo bar
baz
quux zot

Line error in shell script

I have the following code in a shell script.
This only seems to work when it is not defined in a function.
The problematic line is the one containing the "<<".
The error message is
"./run: line 210: syntax error:
unexpected end of file"
How can I write this correctly within a function?
init_database()
{
cd ../cfg
db.sh << ENDC
$DB_ADMIN
0
y
n
ENDC
check_status
sqlplus $DB_SCHEMA#$DB_NAME < initial_data.sql
cd -
}
There are a number of ways to fix that problem.
1/ Unindent the here document end marker, such as:
cat <<EOF
hello
$PWD
EOF
but that will make your code look ugly.
2/ "Indent" the here document begin marker:
cat <<' EOF'
hello
$PWD
EOF
where that bit before the first EOF is exactly the same as the before the second (tab, four spaces, two tabs, whatever). This allows you to keep your nice indenting, although it doesn't expand variables inside the here-document ($PWD doesn't change).
3/ Allow tabs to be stripped from the start of input lines and the end marker.
cat <<-EOF
hello
$PWD
EOF
but there's no way to get tabs into the beginnings of lines.
4/ For your purposes, you can also use:
( echo "$DB_ADMIN";
echo "" ;
echo "0" ;
echo "y" ;
echo "n"
) | db.sh
check_status
sqlplus $DB_SCHEMA#$DB_NAME < initial_data.sql
cd -
I believe number 4 is the best option for you. It allows nice lining up of the input, tabs and spaces anywhere in the lines and variable expansion.
The end of your "Here document" needs to be unindented, I'm afraid.
The ENDC label must be alone in a line without leading/trailing whitspaces.

Resources