Pass options to command in function in Bash - 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 - "$#"
}

Related

using dmenu to define variables

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.

Pass contents of LaTeX statement to command

I can extract the contents of all the \include statements from a latex file (and append ".tex" to each one) with
grep -P "\\\\include{" Thesis_master.tex |sed -n -e"s/\\\\include{/$1/" -e" s/}.*$/.tex/p"
(I couldn't get lookbehinds working in grep, hence the pipe through sed. That gives me a list of filenames, one per line. I'd now like to pass those files to aspell. But aspell only accepts one filename as an argument, so I can't just tack |xargs aspell -c on the end.
I've read this related question but that reads from a file line by line through xargs. So how do I get it to read from the output of sed line by line?
I think xargs -L 1 should do what you need:
grep -P "\\\\include{" Thesis_master.tex | \
sed -n -e"s/\\\\include{/$1/" -e" s/}.*$/.tex/p" | \
xargs -L 1 aspell -c
(Backslash line continuation added for readability)
This will cause xargs to call aspell exactly once per line from the sed pipe.
Since your aspell commands appear to exit with a 255 code, this causes xargs to stop. You could trick xargs into not exiting by doing something like:
grep -P "\\\\include{" Thesis_master.tex | \
sed -n -e"s/\\\\include{/$1/" -e" s/}.*$/.tex/p" | \
xargs -L 1 -I % bash -c "aspell -c %; true"
This will run aspell in a subshell, followed by the true command which always return a 0 exit code to xargs.
The grep recipe is:
grep -oP '\\include{\K.+?(?=})' latex.file | xargs aspell ...

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.

How to remove inherited functions in sh (posix)

How do I ensure that there are no unexpected functions inherited from the parent when my script is run? If using bash,
#!/bin/bash -p
will do the trick, as will invoking the script through env -i. But I cannot rely on the user to invoke env, I don't want to rely on bash, I don't want to do an exec-hack and re-exec the script, and
#!/usr/bin/env -i sh
does not work.
So I'm looking for a portable way (portable == posix) to ensure that the user hasn't defined functions that will unexpectedly modify the behavior of the script. My current solution is:
eval $( env | sed -n '/\([^=]*\)=(.*/s//\1/p' |
while read -r name; do echo unset -f $name\;; done )
but that's pretty ugly and of dubious robustness. Is there a good way to get the functionality that 'unset -f -a' should provide?
edit
Slightly less ugly, but no better (I don't like parsing the output of env):
unset -f $( env | sed -n '/\([^=]*\)=(.*/s//\1/p' | tr \\012 \ )
#!/bin/bash --posix
results in:
SHELLOPTS=braceexpand:hashall:interactive-comments:posix
same as:
#!/bin/sh
SHELLOPTS=braceexpand:hashall:interactive-comments:posix
and "sh" is posix...
EDIT:
tested a few functions - unset was not required in my case...
EDIT2:
compare output of "set", not just "env"
EDIT3:
the following example - output of both "set|wc" also gives same results:
#!/bin/sh
set
set|wc
unset -f $( env | sed -n '/\([^=]*\)=(.*/s//\1/p' | tr \\012 \ )
set
set|wc
How about using the following env shebang line that sets a reasonable PATH variable to invoke the sh interpreter:
#!/usr/bin/env -i PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/xpg4/bin sh

Resources