bash read -s not working when combined with -t? - bash

I have a bash script problem. I am doing while loop and inside this while loop I would like to read with timeout and if input is "q" leave while loop, but I would like to hide input("q").
#!/bin/bash
while [[ "$input" != "q" ]]; do
read -s -t 0.1 -n 1 input
done
I know that -s is supose to hide that but when I combine it with -t 0.1 it doesn't work. Although -s combined with -n works. Is there any solution to this?

Related

Shell script can read file line by line but not perform actions for each line

I'm trying to run this command over multiple machines
sshpass -p 'nico' ssh -o 'StrictHostKeyChecking=no' nico#x.x.x.x "mkdir test"
The IPs are stored in the following .txt file
$ cat ips.txt
10.0.2.15
10.0.2.5
I created a bash script that reads this file line by line. If I run it with an echo:
#!/bin/bash
input="ips.txt"
while IFS= read -r line
do
echo "$line"
#sshpass -p 'nico' ssh -o 'StrictHostKeyChecking=no' nico#$line "mkdir test"
done < "$input"
It prints every line:
$ ./variables.sh
10.0.2.15
10.0.2.5
This makes me understand that the script is working as intended. However, when I replace the echo line with the command I want to run for each line:
#!/bin/bash
input="ips.txt"
while IFS= read -r line
do
#echo "$line"
sshpass -p 'nico' ssh -o 'StrictHostKeyChecking=no' nico#$line "mkdir test"
done < "$input"
It only performs the action for the first IP on the file, then stops. Why?
Managed to solve this by using a for instead of a while. Script ended up looking like this:
for file in $(cat ips.txt)
do
sshpass -p 'nico' ssh -o 'StrictHostKeyChecking=no' nico#$file "mkdir test"
done
While your example is a solution that works, it's not the explanation.
Your could find the explanation here : ssh breaks out of while-loop in bash
In two words :
"while" loop continue reading from the same file-descriptor that defined in the loop header ( $input in your case )
ssh (or sshpass) read data from stdin (but in your case from file descriptor $input). And here is the point that hide the things as we didn't exect "ssh" to read the data.
Just to understand the problem you could have same strange experience for example using commands like "ffmpeg" or "mplayer" in while loop. Mplayer and ffmpeg use the keyboards while they are running, so they will consume all the the file-descriptor.
Another good and funny example :
#!/bin/bash
{
echo first
for ((i=0; i < 16384; i++)); do echo "testing"; done
echo "second"
} > test_file
while IFS= read -r line
do
echo "Read $line"
cat | uptime > /dev/null
done < test_file
At first part we write 1st line : first
16384 lines : testing
then last line : second
16384 lines "testing" are equal to 128Kb buffer
At the second part, the command "cat | uptime" will consume exactly 128Kb buffer, so our script will give
Read first
Read second
As solution, as you did, we could use "for" loop.
Or use "ssh -n"
Or playing with some file descriptor - you could find the example in the link that I gave.

Bash script support piping data into it

I have a bash script that I want to expand to support piping json into.
Example:
echo '{}' | myscript store
So, I tried the following:
local value="$1"
if [[ -z "$value" ]]; then
while read -r piped; do
value=$piped
done;
fi
Which works in a simple case above, but doing:
cat input.json | myscript store
Only get's the last line of the file input.json, it does not handle every line.
How can I support all cases of piping?
The following works:
if [[ -z "$value" && ! -t 0 ]]; then
while read -r piped; do
value+=$piped
done;
fi
The trick was using += and also checking ! -t 0 which checks if we are piping.
If you want to behave like cat, why not use it?
#! /bin/bash
value="$( cat "$#" )"

Bash descriptors in sub shell

The snippet below was intended to listen and select a few songs from a directory:
exec 3<&1
find /some/directory -name '*.mp3' -print0 | xargs -0 bash -c '
for i; do
mplayer -ss 10 "$i" 1<&3
read -p "Select? (y/n)" -n 1 choice 1<&3
if [ "$choice" = "y" -o "$choice" = "Y" ]; then
echo "$i" > /tmp/selected_songs.txt
fi
done
'
exec 3<&-
The intention was to have mplayer and shell read accept input from the keyboard, but ins't working out! For that effect, I thought FD 3 will point to keyboard input for both find and xargs processes. This again gets passed on to the shell that xargs execs, where mplayer and shell read was executed; but it didn't!
What's going wrong here?
First of all, I think you're making this harder than it has to be. If you have bash 4, use globstar.
shopt -s globstar
for i in /some/directory/**/*.mp3; do
mplayer -ss 10 "$i"
read -p "Select? (y/n)" -n 1 choice
case "$choice" in
[yY]) echo "$i" >> /tmp/selected_songs.txt;;
esac
done
Even if you don't, you can do this using find … -exec bash -c 'yourscript' _ {} + instead of using xargs.
(I also changed your > to >> because I assumed you didn't want to truncate the file at each pass.)
As for understanding the problem, there's all kinds of complex things going wrong here, but I'll point out a few important things:
Standard Input is FD 0, but exec 3<&1 duplicates 1 as 3 and opens it for reading.
You seem to be trying to change where keyboard input is going. That's tricky, because doing that is similar to sending a EOF to an interactive shell. Most shells will close when they encounter that. Instead, consider changing where xargs gets its input, and leave the keyboard alone. (BSD xargs has the -o option that is relevant. Check your manpage.)
You are doing the same redirect for both mplayer and read without a timeout. If you're looping and reading, how do you know where you are in the loop when you provide input?
Silly me! It's the FD 0 -- not 1 -- that should be redirected. A version that works as intended below:
exec 3<&0
find /media/jeenu/USB/ -type f -print0 | xargs -0 bash -c '
for i; do
mplayer -ss 10 "$i"
read -p "Select? (y/n/q)" -n 1 choice
case "$choice" in
[yY]) echo "$i" >> /tmp/selected_songs.txt;;
[qQ]) break;;
esac
done 0<&3
'
exec 3<&-
If you want a subshell with bash then you can do this:
find /media/jeenu/USB/ -type f -print0 | xargs -0 bash <<EOF
#This is a bash subshell
#put your code here
EOF

Lynx is stopping loop?

I'll just apologize beforehand; this is my first ever post, so I'm sorry if I'm not specific enough, if the question has already been answered and I just didn't look hard enough, and if I use incorrect formatting of some kind.
That said, here is my issue: In bash, I am trying to create a script that will read a file that lists several dozen URL's. Once it reads each line, I need it to run a set of actions on that, the first being to use lynx to navigate to the website. However, in practice, it will run once perfectly on the first line. Lynx goes, the download works, and then the subsequent renaming and organizing of that file go through as well. But then it skips all the other lines and acts like it has finished the whole file.
I have tested to see if it was lynx causing the issue by eliminating all the other parts of the code, and then by just eliminating lynx. It works without Lynx, but, of course, I need lynx for the rest of the output to be of any use to me. Let me just post the code:
!#/bin/bash
while read line; do
echo $line
lynx -accept_all_cookies $line
echo "lynx done"
od -N 2 -h *.zip | grep "4b50"
echo "od done, if 1 starting..."
if [[ $? -eq 0 ]]
then ls *.*>>logs/zips.log
else
od -N 2 -h *.exe | grep "5a4d"
echo "if 2 starting..."
if [[ $? -eq 0 ]]
then ls *.*>>logs/exes.log
else
od -N 2 -h *.exe | grep "5a4d, 4b50"
echo "if 3 starting..."
if [[ $? -eq 1 ]]
then
ls *.*>>logs/failed.log
fi
echo "if 3 done"
fi
echo "if 2 done"
fi
echo "if 1 done..."
FILE=`(ls -tr *.* | head -1)`
NOW=$(date +"%m_%d_%Y")
echo "vars set"
mv $FILE "criticalfreepri/${FILE%%.*}(ZCH,$NOW).${FILE#*.}" -u
echo "file moved"
rm *.zip *.exe
echo "file removed"
done < "lynx"
$SHELL
Just to be sure, I do have a file called "lynx" that contains the urls separated by a return each. Also, I used all those "echo"s to do my own sort of debugging, but I have tried it with and without the echo's. When I execute the script, the echo's all show up...
Any help is appreciated, and thank you all so much! Hope I didn't break any rules on this post!
PS: I'm on Linux Mint running things through the "terminal" program. I'm scripting with bash in Gedit, if any of that info is relevant. Thanks!
EDIT: Actually, the echo tests repeat for all three lines. So it would appear that lynx simply can't start again in the same loop?
Here is a simplified version of the script, as requested:
!#/bin/bash
while read -r line; do
echo $line
lynx $line
echo "lynx done"
done < "ref/url"
read "lynx"
$SHELL
Note that I have changed the sites the "url" file goes to:
`www.google.com
www.majorgeeks.com
http://www.sophos.com/en-us/products/free-tools/virus-removal-tool.aspx`
Lynx is not designed to use in scripts because it locks the terminal. Lynx is an interactive console browser.
If you want to access URLs in a script use wget, for example:
wget http://www.google.com/
For exit codes see: http://www.gnu.org/software/wget/manual/html_node/Exit-Status.html
to parse the html-content use:
VAR=`wget -qO- http://www.google.com/`
echo $VAR
I found a way which may fulfilled your requirement to run lynx command in loop with substitution of different url link.
Use
echo `lynx $line`
(Echo the lynx $line in single quote('))
instead of lynx $line. You may refer below:
your code
!#/bin/bash
while read -r line; do
echo $line
lynx $line
echo "lynx done"
done < "ref/url"
read "lynx"
$SHELL
try on below
!#/bin/bash
while read -r line; do
echo $line
echo `lynx $line`
echo "lynx done"
done < "ref/url"
I should have answered this question a long time ago. I got the program working, it's now on Github!
Anyway, I simply had to wrap the loop inside a function. Something like this:
progdownload () {
printlog "attmpting download from ${URL}"
if echo "${URL}" | grep -q "http://www.majorgeeks.com/" ; then
lynx -cmd_script="${WORKINGDIR}/support/mgcmd.txt" --accept-all-cookies ${URL}
else wget ${URL}
fi
}
URL="something.com"
progdownload

How to implement a timer keypress in bash?

Here's what will happen, a message is displayed with a specified time waiting for keypress, if no keypress then it will resume.
Example
"Press ESC to exit, otherwise you will die.. 3..2..1"
"Press 'x' to procrastinate and check email, read some blogs, facebook, twitter.. otherwise you will resume work for 12 hours.. 3..2..1"
This should be a really handy function. How do I create this functionality in bash?
Look on the bash man page for the "read" command and notice the "-t timeout" option. Something like this should get you started
for i in 3 2 1 ; do
read -p $i... -n 1 -t 1 a && break
done
Use the -t and -n options of the read bash builtin command, also do not forget -r and -s. (see the manual for details)
#!/bin/bash
timeout=3
echo -n "Press ESC to exit, otherwise you will die..."
while [ $timeout -gt 0 ]; do
echo -n " $timeout"
if read -n1 -t1 -r -s x; then
echo
exit 0
fi
let timeout--
done
echo

Resources