using dmenu to define variables - bash

i'm trying to write a simple script to cut a segment of an audio file, set it to an image and render it to a video.
i want to use dmenu as a clean way of displaying my prompts for inputting variables.
all works well except it seems dmenu expects something to be piped into it, otherwise the script doesn't work.
here is how i have the relevant part of the code currently working
INPUT=$(ls | dmenu -i -p "Input file:")
START=$(echo " " | dmenu -i -p "Start time:")
DURATION=$(echo " " | dmenu -i -p "Duration:")
IMAGE=$(ls | dmenu -i -p "Image file:")
i would like to remove the echo command for $START and $DURATION, both because it is ugly (it leaves an unneeded coloured box of space in dmenu) and because it seems redundant. however, without it the script fails on these lines.
does dmenu always expect something to be piped into it?
perhaps i am doing something else completely wrong, this is only my 3rd ever shell script.

To get rid of colored spaces you can do this:
DURATION=$(:| dmenu -i -p "Duration:")
or
DURATION=$(dmenu -i -p "Duration:"< <(:))
: means true. So it's the same as writing:
DURATION=$(true| dmenu -i -p "Duration:")
DURATION=$(dmenu -i -p "Duration:"< <(true))
But you can also use the -n flag to echo for the same resultat:
DURATION=$(echo -n ''|dmenu -i -p "Duration:")
DURATION=$(dmenu -i -p "Duration:"< <(echo -n ''))
From the man page:
... which reads a list of newline-separated items from stdin
So yes, dmenu need stuff to be piped to it.

Related

Pass options to command in function in Bash

I want to pass command options to a command in function.
For example,
I have a following script, which works with youtube-dl, stream youtube video URL with vlc media player.
youtube-stream(){
youtube-dl -i -o - "$(cuturlquerystr "$1" | perl -pe "chomp")" | vlc.exe -
}
vlc has a number of command line options. I want to use those options from youtube-stream command.
Something like,
youtube-stream --pitch-shift="1.5" --rate="1.1" "https://www.youtube.com/watch?v=RVea-2Up8xM"
The above command means,
youtube-dl -i -o - "$(cuturlquerystr "$1" | perl -pe "chomp")" | vlc.exe - --pitch-shift="1.5" --rate="1.1"
I'd like to implement that by something like following.
youtube-stream(){
youtube-dl -i -o - "$(cuturlquerystr "$1" | perl -pe "chomp")" | vlc.exe - ${options}
}
I know getopt parsing method but I want to implement that by a small amount of code as possible as simple. Any idea?
Save the first argument in a variable, shift it out of the argument list, then you can pass all the remaining arguments to vlc.exe with "$#".
youtube-stream(){
local url=$1
shift
youtube-dl -i -o - "$(cuturlquerystr "$url" | perl -pe "chomp")" | vlc.exe - "$#"
}

Bash output from expect script to two different files

I am trying to output to two different files using tee. My first file will basically be tail -f /myfile and my second output will be a subset of the first file. I have looked online that they were saying we can use `|
tee >(proc1) >(proc2)
I have tried the above but both my files are blank.
Here is what i have so far:
myscript.sh
ssh root#server 'tail -f /my/dir/text.log' | tee >(/mydir/my.log) >(grep 'string' /mydir/my.log > /mydir/mysecond.log)
myexpect.sh
#!/usr/bin/expect -f
set pass password
spawn /my/dir/myexpect.sh
expect {
"key fingerprint" {send "yes/r"; exp_contiue}
"assword: " {send "$pass\r"}
}
interact
In your script, there are some problems in the usage of tee,
tee >(/mydir/my.log): can be substitute with tee /mydir/my.log, since tee would write to stdout and files, i.e. /mydir/my.log
grep 'string' /mydir/my.log > /mydir/mysecond.log: as I mentioned, tee would also write to stdout, so no need to grep the string from file, you can grep from stdout directly. Use pipeline to do it.
So the whole command shall be modified as followed,
ssh root#server 'tail -f /my/dir/text.log | tee /mydir/my.log | grep --line-buffered "string" > /mydir/mysecond.log'
Edit:
For your further question
The command would hang because of tail -f was still waiting for output the growing file. If you don't want the command hanged, try to remove -f for tail.
Depends on the option -f existed for tail, you shall use two different way to allow the grep write file.
For tail case: grep can successfully write file
For tail -f case: --line-buffered for grep would use line buffering on output

How to redirect stdin to a FIFO with bash

I'm trying to redirect stdin to a FIFO with bash. This way, I will be able to use this stdin on an other part of the script.
However, it doesn't seem to work as I want
script.bash
#!/bin/bash
rm /tmp/in -f
mkfifo /tmp/in
cat >/tmp/in &
# I want to be able to reuse /tmp/in from an other process, for example :
xfce4-terminal --hide-menubar --title myotherterm --fullscreen -x bash -i -c "less /tmp/in"
Here I would expect , when I run ls | ./script.bash, to see the output of ls, but it doesn't work (eg the script exits, without outputing anything)
What am I misunderstanding ?
I am pretty sure that less need additional -f flag when reading from pipe.
test_pipe is not a regular file (use -f to see it)
If that does not help I would also recommend to change order between last two lines of your script:
#!/bin/bash
rm /tmp/in -f
mkfifo /tmp/in
xfce4-terminal --hide-menubar --title myotherterm --fullscreen -x bash -i -c "less -f /tmp/in" &
cat /dev/stdin >/tmp/in
In general, I avoid the use of /dev/stdin, because I get a lot of surprises from what is exactly /dev/stdin, especially when using redirects.
However, what I think that you're seeing is that less finishes before your terminal is completely started. When the less ends, so will the terminal and you won't get any output.
As an example:
xterm -e ls
will also not really display a terminal.
A solution might be tail -f, as in, for example,
#!/bin/bash
rm -f /tmp/in
mkfifo /tmp/in
xterm -e "tail -f /tmp/in" &
while :; do
date > /tmp/in
sleep 1
done
because the tail -f remains alive.

Write mplayer's output to fifo and read it

I'm trying write simple notify app in bash. I want to read output from mplayer, parse it and display through notify-send.
I can get desired info from mplayer using this:
mplayer <url> | grep ICY
and then parse in using sed.
I create named pipe, tell mplayer to write it and then I'm reading from it. Unfortunately, it doesn't work. Here's my script:
$fifo=~/.rp/fifo
mkfifo $fifo
mplayer <url> 2>/dev/null | grep ICY 1> $fifo &
while read line < $fifo; do
echo $line
done
wait
Program keeps waiting to input from $fifo. I tried following in other terminal, while this script is running:
Run
echo "Test" > .rp/fifo
Terminal with running script shows "Test"
Run
echo "ICY" | grep ICY > .rp/fifo
also works.
Run
mplayer <url> | grep ICY > .rp/fifo
and it doesn't work.
Is I said above, the combination of mplayer | grep works fine. grep > $fifo works fine. I don't understand why mplayer | grep > $fifo doesn't work.
I suspect you might be experiencing the C library's fully buffered mode for streams. You don't say that you're running the GNU userspace, but if you are, you can look into stdbuf(1) to modify the buffering regime.
You might try first running just grep as a child of stdbuf(1), like this:
mplayer <url> | stdbuf -o L grep ICY > .rp/fifo
If that doesn't work, moar is bettar!
stdbuf -o 0 mplayer <url> | stdbuf -o L grep ICY > .rp/fifo
And if that still doesn't work, then it's possible that mplayer isn't writing to stdout, but directly to /dev/tty. In which case, you will need to read up on expect(1).
You could do unbuffered grep with:
$ mplayer ... 2>&1 | grep --line-buffered "ICY"
or better:
$ mplayer ... 2>&1 | sed -une 's/^.*ICY[^:]*: //p'
or even, why not (sed is very nice for grep and formatting),
this will grep ICY lines and even split line containing - in a first field of 30 chars length separed by a : from a second field:
$ mplayer ... 2>&1 |
sed -une "
/ICY/{
s/^.*ICY[^:]*:.*'\([^']*\)';/\1/;
s/^\(.*\) - /\1 - /;
s/^\(.\{30\}\) *- /\1: /;
p;
}"
could give something like:
Artist name : Song title
Other artist : Other song
Unsplited line
Artist : Title
I start mplayer in slave mode, using FIFO file.
mkfifo /tmp/mpfifo
mplayer -slave -input file=/tmp/mpfifo video.mp4
I am able to control the video player from another terminal.
echo "pause" >> /tmp/mpfifo
echo "volume 50" > /tmp/mpfifo
I want to get value (for example current position of playing video). So I tried:
echo "get_time_pos" > /tmp/mpfifo
But no value returned.
I searched for hours, but no success.
Then I thought to redirect mplayer output to a file:
mplayer -slave -input file=/tmp/mpfifo video.mp4 > /tmp/mpout.txt
After that when like following commands executed:
echo "get_time_pos" > /tmp/mpfifo
echo "get_property length" > /tmp/mpfifo
The outputs in /tmp/mpout.txt was like:
.......
.......
ANS_TIME_POSITION=113.6
ANS_length=2534.602031
If the result of each command would return to the command line would be very nice. Even it may need some works, the output file can be parsed, though.

Replace script works if I type manually but not in script

I have a bash script, replace.sh with the following contents:
ack-grep -a -l -i --print0 --text "$1" | xargs -0 -n 1 sed -i -e 's/$1/$2/g'
When I try and run it as, eg:
replace.sh something somethingnew
The prompt returns without errors but no changes have been made to any files.
If I manually type:
ack-grep -a -l -i --print0 --text "something" | xargs -0 -n 1 sed -i -e 's/something/somethingelse/g'
The files get changed as expected.
Ths $1 syntax seems to work for other scripts I've written. I'm guessing I'm missing something to do with escaping the args or something?
Thanks!
Ludo.
Variable substitutions aren't done in single quotes, try:
ack-grep -a -l -i --print0 --text "$1" | xargs -0 -n 1 sed -i -e "s/$1/$2/g"
See the bash man page section on QUOTING.
Use "" instead of '' in the sed expression. It will not prevent the variablename-resolving. What you are actually doing now is replacing $1 to $2. You can test in console (without writing a script) like this:
$ a=something
$ b=somethingelse
$ sed 's/$a/$b/g' testfile
$ sed "s/$a/$b/g" testfile
This isn't related to your question, but some help on using ack.
The -a and --text conflict with each other. -a will give you a superset of --text. Use one or the other.
Also, it looks like you might as well use grep -Z instead of ack since you're not using any of ack's functionality that is a superset of grep.
In general, if you're using ack in a pipeline, you should probably be using good ol' grep instead.

Resources