put variables from shell inside ack-grep using alias - alias

I want to use ack-grep alias in .zshrc. Here is how my alias lookes like now:
alias 'ack'='ack-grep -C -i -G --match'
but what I need is to put strings from this:
% ack string1 string2
inside
alias 'ack'='ack-grep -C -i -G string1 --match string2'
How to make this?
Update
Here is the code that worked:
ack() {
case $# in
1) args="$1";;
2) args="$1 -G $2";;
*) args="$#";;
esac
eval "ack-grep -iC $args"
}
In my case I need to use eval for using more than one variable.
Update 2
Updated code without security issues:
ack () {
if (( $# == 2 )); then
ack-grep -iC "$1" -G "$2"
elif (( $# == 1 )); then
ack-grep -iC "$1"
else
echo 'Sorry, function works only for one and two params. In other case use ack-grep. Reread .zshrc'
fi
}

eval is rarely necessary.
ack () {
if (( $# > 2 )); then
ack-grep -iC "$#"
else
ack-grep -iC "$1" ${2:+-G "$2"}
fi
}
Update: here is an exact version of your first solution, without eval.
ack() {
case $# in
1) ack-grep -iC "$1";;
2) ack-grep -iC "$1" -G "$2";;
*) ack-grep -iC "$#";;
esac
}

Use a function:
ack() { ack-grep -C -i -G $1 --match $2; }
You will probably want to make it slightly more robust. Something like:
ack() {
case $# in
1) args="-G $1";;
2) args="-G $1 --match $2";;
*) args="$#";;
esac
ack-grep -C -i "$args"
}
Aliases do not provide adequate functionality for this sort of thing, and functions should almost always be used instead. (In fact, you can omit "almost" from the preceding.)

Related

if-else on arguments in npm run script

I would like to call different other scripts, depending on whether a paramter is given or not:
"paramtest": "if [ -z $1 ]; then echo Foo $1; else echo Bar; fi",
npm run paramtest
should give "Bar".
npm run paramtest -- whatever
should give "Foo whatever".
However in practice I only get: (The parameter is added to the whole line, not 'passed in')
> if [ -z $1 ]; then echo Foo; else echo Bar; fi "whatever
sh: 1: Syntax error: word unexpected
What can I do better?
Essentially I am after running full test suite / only individual test with the same command...
"test" : "if [ -z $1 ]; then mocha ./test/**/*.test.js; else mocha $1
Wrapping it in a shell function should do the trick:
"test": "f() { if [ $# -eq 0 ]; then mocha './test/**/*.test.js'; else mocha -- \"$#\"; fi; }; f"
Note that I changed the if condition and the else branch slightly so you can specify multiple file arguments if necessary.
A more succinct method:
"test": "f() { mocha -- \"${#:-./test/**/*.test.js}\"; }; f"
Using a shell function this way might look familiar, as the same technique is often used for git aliases.
Detailed Explanation
Let's use this script for demonstration:
"scripts": {
"myscript": "if [ \"$1\" = one ]; then printf %s\\\\n \"$#\"; else echo false; fi"
}
Here if the first argument is "one", we print all the arguments, and otherwise we print "false". We are of course assuming that npm run-script is using an sh-like shell, and not, e.g., Windows' cmd.exe.
I can't see anything in the npm documentation specifically detailing how arguments are passed to the script, so let's take a look at the source code (npm v6.14.7 at the time of writing). It seems that the script is joined with its arguments here and is then executed here. Essentially, npm run myscript -- one two three becomes
sh -c 'if [ "$1" = one ]; then printf %s\\n "$#"; else echo false; fi "one" "two" "three"'
Our arguments one two three are simply quote-escaped and concatenated to the script command. In terms of the shell grammar, this means that they are ending up as arguments to fi. sh of course rejects this because fi is just a builtin to end if and takes no arguments.
Our goal is something more like
sh -c 'if [ "$1" = one ]; then printf %s\\n "$#"; else echo false; fi' sh "one" "two" "three"
Here one, two, and three are arguments to sh itself and thus become the argument variables $1, $2, and $3 in the given script. npm doesn't let us do this directly, but we can accomplish the same thing by wrapping our script in a shell function:
"scripts": {
"myscript": "f() { if [ \"$1\" = one ]; then printf %s\\\\n \"$#\"; else echo false; fi; }; f"
}
The script here ends with an invocation of the function, so npm will end up concatenating the arguments to this invocation, ultimately calling the function as f "one" "two" "three":
sh -c 'f() { if [ "$1" = one ]; then printf %s\\n "$#"; else echo false; fi; }; f "one" "two" "three"'

how to commentmultiple lines with awk or sed?

I have a perl program which I am downloading and using in a docker file.
This program is calling tbl2asn but I do not need the output and this part runs very long. Therefore, I want to comment a few lines out of this perl program.
runcmd(
"tbl2asn -V b -a r10k -l paired-ends -M n -N 1 -y 'Annotated using $EXE $VERSION from $URL' -Z".
" \Q$outdir/$prefix.err\E -i \Q$outdir/$prefix.fsa\E 2> /dev/null"
);
delfile("$outdir/errorsummary.val");
delfile( map { "$outdir/$prefix.$_" } qw(dr fixedproducts ecn val) );
msg("Repairing broken .GBK output that tbl2asn produces...");
runcmd("sed 's/COORDINATES: profile/COORDINATES:profile/' < \Q$outdir/$prefix.gbf\E > \Q$outdir/$prefix.gbk\E");
delfile("$outdir/$prefix.gbf");
These are the lines I want to comment and at least this line "tbl2asn -V b -a r10k -l paired-ends -M n -N 1 -y 'Annotated using $EXE $VERSION from $URL' -Z". only occurs once in the perl program.
How can I use awk or sed to comment this line via a regex, one before and eight lines after?
Thank you.
Awk solution:
Sample file.pl:
some text
runcmd(
"tbl2asn -V b -a r10k -l paired-ends -M n -N 1 -y 'Annotated using $EXE $VERSION from $URL' -Z".
" \Q$outdir/$prefix.err\E -i \Q$outdir/$prefix.fsa\E 2> /dev/null"
);
delfile("$outdir/errorsummary.val");
delfile( map { "$outdir/$prefix.$_" } qw(dr fixedproducts ecn val) );
msg("Repairing broken .GBK output that tbl2asn produces...");
runcmd("sed 's/COORDINATES: profile/COORDINATES:profile/' < \Q$outdir/$prefix.gbf\E > \Q$outdir/$prefix.gbk\E");
delfile("$outdir/$prefix.gbf");
some text
awk '/"tbl2asn -V b .+ -Z"\.$/{ n = NR + 9 }NR <= n{ rec = "#"rec }
rec{ print rec }{ rec = $0 }
END{ print rec }' file.pl
The output:
some text
#runcmd(
# "tbl2asn -V b -a r10k -l paired-ends -M n -N 1 -y 'Annotated using $EXE $VERSION from $URL' -Z".
# " \Q$outdir/$prefix.err\E -i \Q$outdir/$prefix.fsa\E 2> /dev/null"
#);
#delfile("$outdir/errorsummary.val");
#delfile( map { "$outdir/$prefix.$_" } qw(dr fixedproducts ecn val) );
#
#msg("Repairing broken .GBK output that tbl2asn produces...");
#runcmd("sed 's/COORDINATES: profile/COORDINATES:profile/' < \Q$outdir/$prefix.gbf\E > \Q$outdir/$prefix.gbk\E");
#delfile("$outdir/$prefix.gbf");
some text

awk script replace 2nd occurrence of string inside double quotes

I'm having trouble making a script to replace a string that is inside double quotes. The files sections looks like this:
"regA~1" : "FF_NODE~94"
"regA~2" : "FF_NODE~105"
"regA~3" : "FF_NODE~116"
"regA~4" : "FF_NODE~127"
"regA~5" : "FF_NODE~138"
"regA~6" : "FF_NODE~149"
"regA~7" : "FF_NODE~154"
"regA~8" : "FF_NODE~155"
"regA~9" : "FF_NODE~156"
"regA~1" : "FF_NODE~95"
"regA~11" : "FF_NODE~96"
It works if I do
awk '/"regA~1"/{c++;if(c==2){sub("regA~1","regA~10");}}1' file > file_out
but when trying to make this a script where I pass a variable regA~1 and the value for c it doesn't work.
s="regA~1";
r="regA~10";
n=2;
awk -v search="$s" -v replace="$r" -v count=$n '/search/{c++;if(c==count){sub(search,replace);}}1' file > file_out
I also tried
awk -v search=$s -v replace=$r -v count=$n '/search/{c++;if(c==count){sub(search,replace);}}1' file > file_out
The syntax you need to match an RE that's stored as a string in a variable is
$0 ~ var
not
/var/
Thanks to Ed Morton for the tip. Here's the bash script in case anyone needs something like this. Not very sophisticated but it works for me.
#!/bin/bash
# Replaces a specific occurrence of a search string with a replace string
if [ $# -lt 4 ] ; then
echo -e "Wrong number of parameters."
echo -e "Usage:"
echo -e "repnthstr file search replace n"
echo -e "repnthstr fileext search replace n"
exit 1
fi
for file in $1
do
if [ -f $file -a -r $file ]; then
awk -v search=$2 -v replace=$3 -v cnt=$4 '$0 ~ search{c++;if(c==cnt){sub(search,replace);}}1' "$file" > temp && mv temp "$file"
else
echo "Error: Cannot read $file"
fi
done

Match pattern for variable in Shell Programming

it seems that I keep getting -gt or == error with the following. Can someone help?
flag= echo $flightSeatBooked | awk -F[,] '{print match($flightSeatBooked, $orderSeats)}'
if $flag == 0; then
echo "Success";
else
echo "fail";
Given:
flightSeatBooked= 9;,A1,A2,A3,A4,B2,E4,C3,B3,D3,D2,E1,E2,C2,B4,C4,D4,C1,D1,E3,B1
orderSeats= B2 (not found in the variable)
Expected output:
Success
Quite a few mistakes. Change it like this:
flag=$(echo $flightSeatBooked | awk -v flseat=$flightSeatBooked -v orseat=$orderSeats '{print match(flseat, orseat)}')
if [ $flag -eq 0 ]; then
echo "Success";
else
echo "fail";
fi
Command substitution has been done using the $(...) notation.
It is not a good practice to use the shell variables directly in awk, and hence passed shell variables to awk using -v.
The syntax of if used was incorrect, updated to correct it.
This is how to do what you ask:
flag=$(awk -v flseat="$flightSeatBooked" -v orseat="$orderSeats" 'BEGIN{print index(flseat, orseat)}')
if [ $flag -eq 0 ]; then
echo "Success"
else
echo "fail"
fi
BUT I don't think what you ask is a good idea. It at least should be something like this:
awk -v flseat="$flightSeatBooked" -v orseat="$orderSeats" 'BEGIN{exit index(flseat, orseat)}')
if [ $? -eq 0 ]; then
echo "Success"
else
echo "fail"
fi
and all you probably really need is something like this:
case "$flightSeatBooked" in
*"$orderSeats"* ) echo "fail";;
* ) echo "Success" ;;
esac
Check the logic (as I haven't!), but hopefully you get the approach.
You can also use this below to check whether $orderSeats is in $flightSeatBooked. If it is in then it return the length of string that matched or 0 is returned.
expr "$flightSeatBooked" : ".*,${orderSeats},"

How to prompt for target-specific Makefile variable if undefined?

This is similar to another issue, but I only want make to prompt for a value if I'm running a specific target and a mandatory variable has not been specified.
The current code:
install-crontab: PASSWORD ?= "$(shell read -p "Password: "; echo "$$REPLY")"
install-crontab: $(SCRIPT_PATH)
#echo "#midnight \"$(SCRIPT_PATH)\" [...] \"$(PASSWORD)\""
This just results in the following output and no prompt:
Password: read: 1: arg count
#midnight [...] ""
The important point here is that I have to ask only when running this target, and only if the variable has not been defined. I can't use a configure script, because obviously I shouldn't store passwords in a config script, and because this target is not part of the standard installation procedure.
Turns out the problem was that Makefiles don't use Dash / Bash-style quotation, and that Dash's read built-in needs a variable name, unlike Bash. Resulting code:
install-crontab-delicious: $(DELICIOUS_TARGET_PATH)
#while [ -z "$$DELICIOUS_USER" ]; do \
read -r -p "Delicious user name: " DELICIOUS_USER;\
done && \
while [ -z "$$DELICIOUS_PASSWORD" ]; do \
read -r -p "Delicious password: " DELICIOUS_PASSWORD; \
done && \
while [ -z "$$DELICIOUS_PATH" ]; do \
read -r -p "Delicious backup path: " DELICIOUS_PATH; \
done && \
( \
CRONTAB_NOHEADER=Y crontab -l || true; \
printf '%s' \
'#midnight ' \
'"$(DELICIOUS_TARGET_PATH)" ' \
"\"$$DELICIOUS_USER\" " \
"\"$$DELICIOUS_PASSWORD\" " \
"\"$$DELICIOUS_PATH\""; \
printf '\n') | crontab -
Result:
$ crontab -r; make install-crontab-delicious && crontab -l
Delicious user name: a\b c\d
Delicious password: e f g
Delicious backup path: h\ i
no crontab for <user>
#midnight "/usr/local/bin/export_Delicious" "a\b c\d" "e f g" "h\ i"
$ DELICIOUS_PASSWORD=foo make install-crontab-delicious && crontab -l
Delicious user name: bar
Delicious backup path: baz
#midnight "/usr/local/bin/export_Delicious" "a\b c\d" "e f g" "h\ i"
#midnight "/usr/local/bin/export_Delicious" "bar" "foo" "baz"
This code:
treats all input characters as literals, so it works with spaces and backslashes,
avoids problems if the user presses Enter without writing anything,
uses environment variables if they exist, and
works whether crontab is empty or not.
l0b0's answer helped me with a similar problem where I wanted to exit if the user doesn't input 'y'. I ended up doing this:
#while [ -z "$$CONTINUE" ]; do \
read -r -p "Type anything but Y or y to exit. [y/N] " CONTINUE; \
done ; \
if [ ! $$CONTINUE == "y" ]; then \
if [ ! $$CONTINUE == "Y" ]; then \
echo "Exiting." ; exit 1 ; \
fi \
fi
I hope that helps someone. It's hard to find more info about using user input for an if/else in a makefile.