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

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.

Related

How to cut every nth line of file A while read value from file B?

I would like to cut every nth line in file A by the value of file B, line by line.
File A looks like:
#V300059044L3C001R0010004402
AAGTAGATATCATGGAGCCGTTG
+
FGFFGFFGFFGGGGGFFFGGFGF
#V300059044L3C001R0010009240
AAAGGGAGGGAGAATAATGG
+
FGEFDFGGEFFGGEDEGEGF
...
File B looks like:
12
5
4
19
...
I want to cut the fourth line from file A with the first value of file b, the 8th line with the 2nd value of file b and so on.
I am using the command below which cuts line 1, 2, 3, ... instead of 4, 8, 12.
for i in *fileA; do fileB=${i/A/B}; while IFS= read -r a && IFS= read -ru3 b; do printf %s\\n "${b:a}" & [ $( jobs | wc -l ) -ge $( sysctl -n hw.ncpu ) ] && wait; done <$i 3< $fileB > output; done
I used the last part to hurry up the command & [ $( jobs | wc -l ) -ge $( sysctl -n hw.ncpu ) ] && wait (it takes a few hours ...)
If your Linux distribution does not install Ed by default, then you will want to do that for this script. It's a very small program, so there's no reason not to have it.
#!/bin/sh -x
cp file-a.txt stack
next () {
[ -s stack ] && main
end
}
main() {
ed -s stack < file-a.ed
ed -s 4lines.txt < 4lines.ed
next
}
end() {
paste new-a.txt file-b.txt > merged-file.txt
rm -v ./*.ed
rm -v ./4lines.txt
rm -v ./stack
rm -v ./new-a.txt
exit 0
}
cat >> file-a.ed << EOF
1,4W 4lines.txt
1,4d
wq
EOF
cat >> 4lines.ed << EOF
1,3d
1W new-a.txt
1d
wq
EOF
next

Trying to make grep a variable

#!/bin/bash
echo "Please type the file name"
read filename
echo "Please type the word or phrase you wish to look for"
read string
grep '$string' /home/pi/$filename
I was wondering how I could make grep a variable so I could use a code like this:
if [ $var=~$string ];
then
echo "the string is there
else
echo "sorry string doesn't exist"
To assign the output of grep to a variable:
var = $(grep '$string' /home/pi/$filename)
EDIT
As #staticx pointed out, in your case, where you grep for $string and see if the result again matches $string, it would be easier just to see if grep finds the element by piping it wc -l.
count = $(grep '$string' /home/pi/$filename | wc -l)
if [ count -gt 0 ]; then
# do stuff
fi

bash script: apache processlist for specific user where C = 0

I'm writing a bash script and need to retrieve the process list from apache for a specific user where the C value (processor utilization) is zero. I then want to kill just those processes. my script currently looks like this:
process_user=myuser
max_instances=10
poll_interval=60
while true; do
count=$(ps -u $process_user | wc -l)
echo "count: $count"
if [[ $count > $max_instances ]]; then
killall "$process_user"
echo "Found $count $process_user processes. Killed."
fi
sleep "$poll_interval"
done
The above works fine for identifying the processes for a specific user and killing them. But I don't know how to further limit by whether processor utilization is 0.
Here's a solution:
process_user=myuser
max_instances=10
pool_interval=60
while sleep $pool_interval;do
ps -o pid,c -u $process_user --no-headers \
| awk ' \
$2 > 0{top=top " " $1} \
{count++} \
END { \
if(count > '$max_instances' && top){ \
system("kill " top); \
print "killed: " top \
} \
}'
done
Some explanations:
ps -o pid,c -u $user --no-headers
Show pid and processor utilization (c) of processes owned by $user. Skip the header (PID C)
$2 > 0 - this {} block will be executed only for lines where the second field (processor utilization) is greated then 0.
{ top = top ' ' $1 } append pid (first field - $1) to variable top separating with space
{count++} count all the lines = user processes
END { this block will be executed after all the lines were processes

bash check value is integer and in range

I read this stackoverflow question...
Bash: check user input is correct
which does most of what I want however rather then checking it's just an integer I need to check it's an integer in a variable range....
The script looks for files in a directory and then assigns a number to them...
File 1
File 2
File 3
etc....
The user chooses the the number and the script then executes commands against that file.....the variable $FILELIST is the total number of files.
Taking the example from the previous stackoverflow I tried.....
FILENUM=""
while [[ ! ($FILENUM =~ ^[0-$FILELIST]+$) ]]; do
echo " "
echo "Please enter the file number: "
read -p "1 - $FILELIST" FILENUM < /dev/tty
done
echo "$FILENUM"
However this is throwing a syntax error: unexpected "(" (expecting "do") in the while line and I'm not sure why, I suspect $FILELIST has to be bracketed somehow but an explanation as to why the above works would help me understand the problem.
Thanks
bash-specific answers:
You don't need to reinvent the wheel: use the select builtin:
cd /path/to/directory
PS3="Select a file: "
select file in *; do
if [[ $file ]]; then break; fi
done
echo "You selected '$file'"
echo "You selected file number $REPLY"
To check a number is within a certain range, I'd write:
if (( 0 <= $number && $number <= $max )); then echo "in range"; fi
Since you're using ash you might use this as a reference: http://manpages.debian.net/cgi-bin/man.cgi?query=dash
while true; do
FILENUM=""
echo
echo "Please enter the file number: "
read -p "1 - $FILELIST" FILENUM < /dev/tty
if expr "$FILENUM" : '[0-9]\+$' &&
[ $FILENUM -gt 0 ] &&
[ $FILENUM -le $FILELIST ]
then
break
fi
done
echo "$FILENUM"

put variables from shell inside ack-grep using 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.)