LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (http://www.linuxquestions.org/questions/programming-9/)
-   -   How do I pass complicated commands (With bar's) as an argument to another script? (http://www.linuxquestions.org/questions/programming-9/how-do-i-pass-complicated-commands-with-bars-as-an-argument-to-another-script-803351/)

theaceoffire 04-21-2010 02:17 PM

How do I pass complicated commands (With bar's) as an argument to another script?
 
Ok, I have tried figuring out as much as I could on my own, but this is not as obvious as I hoped. I am attempting to script some tasks I have to do, but I have no control over one of the scripts I have to use... and they output all kinds of useless things on the screen.

My goal is simple: Capture all output from their scripts, and create a progress line that only shows the most recent output from their stuff.

So, here was my first solution; a file I called "spin":
Code:

#!/bin/bash
spinX(){
PROC=$1
STRT=`date "+%s"`
while [ -d /proc/$PROC ];do
        last3="`getLine $2`"
        echo -en "\r" "<(''<)  $last3";sleep 0.3
        last3="`getLine $2`"
        echo -en "\r" "<(' ')>  $last3";sleep 0.3
        last3="`getLine $2`"
        echo -en "\r" " (>'')>  $last3";sleep 0.3
        last3="`getLine $2`"
        echo -en "\r" "<(' ')>  $last3";sleep 0.3
done
END=`date "+%s"`
Finished=`printf "%02d:%02d:%02d" $((($END-$STRT)/3600)) $((($END-$STRT)/60%60)) $((($END-$STRT)%60))`
echo -en "\r" "(^-^) Done after [$Finished]";
echo
}

getLine(){
tmp=`tail -1 $1`
if [ "$tmp" != "" ];
        then echo "[$tmp]";
        echo "$tmp" >$1;
        else echo "";
fi;
}

spinX $1 $2

To use it, you pass it a process ID and a file that contains the output from that process. As the process continues, a kurby dances on the screen (To let you know that the process has not hanged), and the tail of the output is shown (To let you know what it is doing). When the process ends, the kurby stops dancing and the time it took is displayed.

And here is the file I call "noise":
Code:

#!/bin/bash
while [ i -lt 100 ];do
        i=1
        echo "Look at me count!$"
        sleep 1
        let "i=$i+1"
done

This does nothing but create random output, for testing. It counts from 1 to 99 on the screen.

To run my test, I do the following:

Code:

( noise ) &>tmp.txt & spin $! tmp.txt
It works relatively well, but it is messy. I don't like creating a temp file, and I don't like the messy syntax for calling my program. I decided that I would rather move everything into the spin program, to make using it less messy:

Code:

#Spin Psuedo code
#$1 = command I am about to run
(exec $1) &>tmp.txt & spinX $! tmp.txt

By executing the process inside of the spin code, I can get rid of the tmp file later on without changing a lot of scripts (Or move it, or whatever). I can also call it by passing the command to the script, which I find more elegant.

So here is what I would like to know:

1) If possible, I would love to get rid of the tmp file all together, and store the most recent line of output from script 1 into a variable that script 2 can print out instead... is it possible?
2) How can I run a random command that is passed as an argument? Basic ones work fine, but anything with a pipe fails me.

Example of a script:
Code:

#!/bin/bash
#myEcho.sh
echo;echo "Recieved command: ";echo $1;echo;
echo "Attempting to run command: ";echo
exec $1

Example code for passing commands to script:
Quote:

> myEcho.sh "ls -al" #works
> myEcho.sh 'ls -al' #works

> myEcho.sh "ls -al|grep *.sh" #fail
# Output:
#ls: invalid option -- |
#Try `ls --help' for more information.

> myEcho.sh "ls -al|grep \"*.sh\"" #fail
# Output:
#ls: invalid option -- |
#Try `ls --help' for more information.

> myEcho.sh 'ls -al|grep *.sh\' #fail
# Output:
#ls: invalid option -- |
#Try `ls --help' for more information.
Anyone have ideas?

Sergei Steshenko 04-21-2010 02:34 PM

Quote:

Originally Posted by theaceoffire (Post 3943041)
...
1) If possible, I would love to get rid of the tmp file all together, and store the most recent line of output from script 1 into a variable that script 2 can print out instead... is it possible?
...

How about the following tiny example:

Code:

sergei@amdam2:~/junk> (stdout_of_command=`echo FOO`; echo stdout_of_command=$stdout_of_command)
stdout_of_command=FOO
sergei@amdam2:~/junk>

- the key item here is a pair of back quotes.

Sergei Steshenko 04-21-2010 02:49 PM

FWIW, your desire not to have temporary files is probably a bad idea.

You assumption apparently is that things work, and my assumption is that (at least, sometimes) they don't. So when things do not work log files of various scripts involved in the process more often than not contain very valuable for postmortem debugging/investigation information.

grail 04-21-2010 09:43 PM

Or you could try:
Code:

#!/bin/bash
#myEcho.sh
echo;echo "Recieved command: ";echo $1;echo;
echo "Attempting to run command: ";echo
eval $1


catkin 04-22-2010 01:21 AM

How about using the script command to capture the output from the script you have no control over?
Code:

script -c the_script.sh -f <temporary file> -q
Run that in a sub-shell. Now your script can continue, displaying the spinner and grabbing lines of output from the temporary file to display.

theaceoffire 04-22-2010 06:48 AM

Thanks for the replies
 
Sergei Steshenko:
Unless I misunderstand your example, I am not sure that work for a script with multiple slow outputs. Is it possible to pipe all output to a script that would continuously replace a variable with the most recent line received?

My current method of &> tmpfile.txt works, but creates a tmpfile, which is slightly annoying.

Also, I am not using the tmpfile as a log, but as a cludge to allow me access to the output of the script without it going to the screen... if I could avoid making random tmp files, it would make me feel better.

Grail
Your script fixes my second, major problem! I can now pass commands like:
Code:

>myEcho "ls -al|grep -E \".*sh\""
And it works! It no longer ignores the pipe!

Thanks!

I don't suppose you guys would know how to pipe a long running script's multiple line value to a variable and access the last line? That would allow me to get rid of my tmp file and (if needed) create a log file..

grail 04-22-2010 08:14 AM

I am not a 100% on this, but I am sure catkin or others will correct me, but would you not just throw it into a file descriptor,
so something along the lines of:
Code:

exec 6<"file1"
but replace the file being used here with the command you are currently putting into the tmp file.

Like i said, not hunj on this and definitely not tested.

theaceoffire 04-22-2010 09:18 AM

So for those who wanted to see my final product:

Code:

#!/bin/sh
#progress.sh
tmpFile="/home/me/script/tmp/tmp.txt"    #We store output of passed command.
fail=0                                  #We have not Ctrl+C'ed yet.
alias wipeRest="tput el;tput cuf 9999"  #Wipe out remaining txt, put cursor far right.
trap theyWantToQuit SIGINT              #If they *do* Ctrl+C, run function 'theyWantToQuit'.

spinX(){
        PROC=$1
        STRT=`date "+%s"`
        while [ -d /proc/$PROC ];do        #While process continues
                tmp=`getLine`;echo -en "\r" "<(''<)  $tmp ";wipeRest;sleep 0.3
                tmp=`getLine`;echo -en "\r" "<(' ')>  $tmp ";wipeRest;sleep 0.3
                tmp=`getLine`;echo -en "\r" " (>'')>  $tmp ";wipeRest;sleep 0.3
                tmp=`getLine`;echo -en "\r" "<(' ')>  $tmp ";wipeRest;sleep 0.3
        done
        END=`date "+%s"`
        Finished=`printf "%02d:%02d:%02d" $((($END-$STRT)/3600)) $((($END-$STRT)/60%60)) $((($END-$STRT)%60))`
        if [ "$fail" == "0" ];then
                echo -en "\r" "(^-^) Done after [$Finished] ";wipeRest;echo
        else    echo -en "\r" "(~.~) Process Canceled after [$Finished]! ";wipeRest;echo
        fi
}

getLine(){
        if [ -f $tmpFile ];then              #We have a tmp file, work with it.
                tmp=`tail -1 $tmpFile`        #Grab the last line.
                echo "[$tmp]"                #Deliver to user.
                echo "$tmp">$tmpFile          #Replace file with last line.
        else echo ""                          #Got nothing.
        fi
}

theyWantToQuit(){                            #They pressed Ctrl+C, close gracefully.
        [ -f $tmpFile ]&&rm $tmpFile          #Remove tmp file if it exists.
        kill $PROC >/dev/null 2>&1 & wait    #Don't show stuff on screen.
        fail=1
}

( eval $1 ) &>$tmpFile & spinX $!

^_^ And to use it:

Code:

> progress.sh "{your commands here}"
> progress.sh "ls -al|grep -E \".*.sh\""

So yeah, it is much easier to use, if you ctrl+c it stops the background process and gets rid of the tmp file while still giving you useful info, and the animation looks smoother since it wipes out the remaining txt on the line. I also moved the cursor *FAR* to the right, so that it won't be constantly moving as the line's width changes.

If anyone can suggest a way of doing this without the tmp file, let me know!

Sergei Steshenko 04-22-2010 09:40 AM

Quote:

Originally Posted by theaceoffire (Post 3943861)
Sergei Steshenko:
Unless I misunderstand your example, I am not sure that work for a script with multiple slow outputs.
...

So you need a multi-threaded script. Possible, for example, in Perl, don't know about 'bash'.


All times are GMT -5. The time now is 05:43 AM.