I found (use '[clojure.java.shell :only [sh]]) for executing shell commands with clojure. Now, while (sh "ls" "-a") does the job, (sh "ls" "-a" "| grep" "Doc") doesn't. What's the trick?
clojure.java.shell/sh executes a command (the first argument passed to sh function) with specified arguments (the rest of the parameters passed to sh).
When you execute:
(sh "ls" "-a" "| grep" "Doc")
you ask to execute ls with parameters -a, | grep and Doc.
When you type ls -a | grep Doc in your terminal then the shell interprets it as executing ls, taking its std out and pass it as std in to another process (grep) that should be started by the shell.
You could simulate what the shell is doing by starting ls as one process, take its std output and then execute grep passing output from ls as its input.
The simpler solution would be to just ask a shell process to execute everything as if it was typed in terminal:
(sh "bash" "-c" "ls -a | grep Doc")
It's important to pass -c and ls ... as separate arguments so bash gets them as a separate parameters. You also need to have the whole command you want to execute as one string (ls -a | grep Doc). Otherwise only the first argument after -c will be treated as a command. For example this won't do what you would like:
(sh "bash" "-c" "ls -a" "|" "grep Doc")
Related
I would like to run the following command from Jenkins:
ssh -i ~/.ssh/company.pem -o StrictHostKeyChecking=no user#$hostname "supervisorctl start company-$app ; awk -v app=$app '$0 ~ "program:company-"app {p=NR} p && NR==p+6 && /^autostart/ {$0="autostart=true" ; p=0} 1' /etc/supervisord.conf > $$.tmp && sudo mv $$.tmp /etc/supervisord.conf”
This is one of the last steps of a job which creates a CloudFormation stack.
Running the command from the target server's terminal works properly.
In this step, I'd like to ssh to each one of the servers (members of ASG's within the new stack) and search and replace a specific line as shown above in the /etc/supervisord.conf, basically setting one specific service to autostart.
When I run the command I get the following error:
Usage: awk [POSIX or GNU style options] -f progfile [--] file ...
Usage: awk [POSIX or GNU style options] [--] 'program' file ...
I've tried escaping the double quotes but got the same error, any idea what I'm doing wrong?
You are running in to this issue due to the way the shell handles nested quotes. This is a use case for a HERE DOCUMENT or heredoc - A HERE DOCUMENT allows you to write multi-line commands passed through bash without worrying about quotes. The structure is as follows:
$ ssh -t user#server.com <<'END'
command |\
command2 |\
END
<--- Oh yeah, the -t is important to the ssh command as it lets the shell know to behave as if being used interactively, and will avoid warnings and unexpected results.
In your specific case, you should try something like:
$ ssh -t -i ~/.ssh/company.pem -o StrictHostKeyChecking=no user#$hostname <<'END'
supervisorctl start company-$app |\
awk -v app=$app '$0 ~ \"program:company-\"app {p=NR} p && NR==p+6 \
&& /^autostart/ {$0="autostart=true" ; p=0} 1' \
/etc/supervisord.conf > $$.tmp && sudo mv $$.tmp /etc/supervisord.conf
END
Just a note, since I can't be sure about your desired output of the command you are running, be advised to keep track of your own " and ' marks, and to escape them accordingly in your awk command as you would at an interactive terminal. I notice the "'s around program:company and I am confused a bit by them If they are a part of the pattern in the string being searched they will need to be escaped accordingly. P.S.
A little context for my project: We have an arbitrary number of files that need a separate process for each file then need to search using an exec() call to find every time a specific KEY is used. I know how to use grep from the command line using this command:
grep -o KEY FILENAME.txt | wc -l > OUTPUT.txt
But I cannot figure out how to do this in c++. I found a thread on here that gave me this line.
execl("/bin/grep","grep",pattern,filename,NULL);
It compiles and runs so I think it works but the problem is I need to output the number of times the pattern occurred to a file and I tried the line below but expectedly it didn't work. It gave this error "grep: out.txt: No such file or directory"
execl("/bin/grep", "grep",pattern,fileName,output,NULL);
Here are the directions of this part of my project.
You can do this by means of the
system call exec() , providing it with the path to the executable of the shell (typically, /bin/sh )
and, as arguments of /bin/sh , the string -c and the string corresponding to the search command
( grep -o ... ).
Some guidance here would be much appreciated!
For the actual execution as you would do on command line would be:
execl("/bin/sh", "/bin/sh", "-c", "grep -o KEY FILENAME.txt | wc -l > OUTPUT.txt")
This will mean that the shell would take the line grep -o KEY FILENAME.txt | wc -l > OUTPUT.txt, interpret it and run it. Note that this will include wild card expansion and all what the shell does.
Then of course if you wan't to continue after it has completed you will have to fork first because execl does not return if it's successful at starting the program (ie bash).
I am trying to generate a subprocess for a gui which will spawn a terminal. I want the data produced by this terminal (stdout and stderr) to be displayed on the window that appears as well as to a set log file. When I type the command directly in to a shell it works as intended, but when provided to a QProcess as a start command it doesn't actually write anything to the file.
For example: if the user doesn't have the binaries installed it should write to bin.log that the command was not found. I have set my environment for the spawned QProcess to be the same as the running parent process (so that it can find bash and anything else), and have set the working directory before calling QProcess.start() method. I wrote a test case which follows:
gnome-terminal --title 'Sub-Terminal' -e 'bash -c "foo [args to foo] |& tee foo.log"'
In Qt I do the following:
QProcess *child = new QProcess(this);
QString cmd = "gnome-terminal --title 'Sub-Terminal' -e 'bash -c \"foo [args to foo] |& tee foo.log\"'";
child->setProcessEnvironment(<process environment I have created before>);
child->setWorkingDirectory(<current working dir>);
child->start(cmd);
...
It generates the terminal but does not write any data to foo.log. I have also tried the following:
QProcess *child = new QProcess(this);
QString prog = "gnome-terminal"
QStringList args;
args << "-x" << "bash" << "foo" << "[foo's arguments]" << "|&" << "tee" << "foo.log";
// set the process env and working dir
child->start(prog, args);
Does anyone have any advice on how to solve this?
I have tried redirecting stdout and stderr using QProcess.setStandardErrorFile(foo.log) and QProcess.setStandardOutputFile(foo.log), but it seems like that would be redirecting the stdout from the gnome-terminal itself (which isn't anything).
You have to provide the whole command to be executed in the terminal as one single argument to QProcess after "-e", as well as the command to be executed in bash as one single argument to bash:
args << "-e" << "bash -c 'foo [args to foo] |& tee foo.log'";
This basically executes
bash -c 'foo [args to foo] |& tee foo.log'
in the terminal, which by itself executes
foo [args to foo] |& tee foo.log
within bash.
You are right with your guess that reading the outputs from the gnome-terminal process doesn't work.
How to pass a regular expression as a parameter to a shell script?
I need to write a shell script, which will take parameters and give them to unix commands. And I'd like to use regular expressions there. Is this possible at all?
Or reformulation - how to write the script equivalent to "cp" command, using only this command?
I'm trying to make a file "mycp"
#!/bin/bash -fx
cp $2 $1
and call it by
mycp myDir "*sh"
and want it to do the same as "cp *sh myDir".
But resulting bash interpretation is:
+ cp '*sh' myDir
cp: *sh: No such file or directory
Revised question
I'm trying to make a file "mycp"
#!/bin/bash -fx
cp $2 $1
and call it by
mycp myDir "*sh"
In that case, you still need eval, but you'd write:
#!/bin/bash -x
eval cp "$2" "$1"
You're running into problems because you have specified the -f option. man bash says (in part):
After word splitting, unless the -f option has been set, bash scans each word for the characters *, ?, and [.
Remove the f from the 'shebang' (first) line of the script.
Original question
Given that you want mycp "*sh*" aa to do shell expansion on the argument, you'll probably end up using eval in your script:
eval cp "$#"
However, the use of eval is dangerous; it can lead to unexpected side-effects. The use of "$#" is important; it preserves the number of arguments and spaces in them. Unfortunately, using eval then undoes that, but we can't have everything — or not easily.
For quite a long time (say 1987* to 1999), I used this script as a cover for cp:
: "#(#)$Id: cp.sh,v 1.3 1997/06/02 21:45:00 johnl Exp $"
#
# Alternative copy command
case $# in
0) /bin/cp ;;
1) /bin/cp $1 . ;;
2) /bin/cp "$#" ;;
*) if [ -d `la "$#"` ]
then /bin/cp "$#"
else /bin/cp "$#" .
fi;;
esac
It uses a very simple C program called la (for 'last argument') to get the last argument and checks whether the given last argument is a directory. More than 99% of the time, if I typed (by accident) 'cp /some/where/sh' rather than cp /some/where/*sh* ., the second was what I meant, and the script fixed things. I haven't used it for quite some time; it may have been in the last millennium, but was probably sometime earlier in this one that I gave up using it.
* Although the version string says '1997', the code is identical to the 1987 version. Version 1.1 and 1.2 were under SCCS and used different SCCS ID strings; the conversion to RCS made them identical. Version 1.3 reinstated the #(#) identifier string used by the SCCS what command to the RCS version handling. So, the script is ancient — 1987, really.
Try this: eval cp $1 $2
Bash reference manual: eval
If all you want to do is specify the directory first, look at the -t option to GNU cp:
alias mycp='cp -t'
mycp mydir *.sh
Otherwise:
mycp() {
local dir=$1
shift
cp "$#" "$dir"
}
mycp mydir *.sh
Either way, let the shell expand the wildcards and avoid the use of eval
(note, *.sh is not a regular expression, it's a shell "pattern", often referred to as a "glob pattern")
I have a extremely complicated shell script, within which it calls a C++ program I want to debug via GDB. It is extremely hard to separate this c++ program from the shell since it has a lot of branches and a lot of environmental variables setting.
Is there a way to invoke GDB on this shell script? Looks like gdb requires me to call on a C++ program directly.
In addition to options mentioned by #diverscuba23, you could do the following:
gdb --args bash <script>
(assuming it's a bash script. Else adapt accordingly)
There are two options that you can do:
Invoke GDB directly within the shell script. This would imply that you don't have standard in and standard out redirected.
Run the shell script and then attach the debugger to the already running C++ process like so: gdb progname 1234 where 1234 is the process ID of the running C++ process.
If you need to do things before the program starts running then option 1 would be the better choice, otherwise option 2 is the cleaner way.
Modify the c++ application to print its pid and sleep 30 seconds (perhaps based on environment or an argument). Attach to the running instance with gdb.
I would probably modify the script to always call gdb (and revert this later) or add an option to call gdb. This will almost always be the easiest solution.
The next easiest would be to temporarily move your executable and replace it with a shell script that runs gdb on the moved program. For example, in the directory containing your program:
$ mv program _program
$ (echo "#!/bin/sh"; echo "exec gdb $PWD/_program") > program
$ chmod +x program
Could you just temporarily add gdb to your script?
Although the answers given are valid, sometimes you don't have permissions to change the script to execute gdb or to modify the program to add additional output to attach through pid.
Luckily, there is yet another way through the power of bash
Use ps, grep and awk to pick-out the pid for you after its been executed. You can do this by either wrapping the other script with your own or by just executing a command yourself.
That command might look something like this:
process.sh
#!/usr/bin/env bash
#setup for this example
#this will execute vim (with cmdline options) as a child to bash
#we will attempt to attach to this process
vim ~/.vimrc
To get gdb to attach, we'd just need to execute the following:
gdb --pid $(ps -ef | grep -ve grep | grep vim | awk '{print $2}')
I use ps -ef here to list the processes and their arguments. Sometimes, you'll have multiple instances of a program running and need to further grep down to the one you want
the grep -ve grep is there because the f option to ps will include the next grep in its list. If you don't need the command arguments for additional filtering, don't include the -f option for ps and ignore this piece
grep vim is where we're finding our desired process. If you needed more filtering, you could just do something like grep -E "vim.*vimrc" and filter down to exactly the process that you're trying to attach to
awk '{print $2}' simply outputs just the process' pid to stdout. Use $1 if you're using ps -e instead of ps -ef
My normal setup is to run such script that starts my process in 1 tmux pane and having typed something similar to the above in a bottom pane. That way if I need to adjust the filtering (for whatever reason), I can do it pretty quickly.
Usually though, it will be the same for a specific instance and I want to just attach automatically after its been started. I'll do the following instead:
runGdb.py
#!/usr/bin/env bash
./process.sh &
PID=$(ps -ef | grep -ve grep | grep -E "vim.*vimrc" | awk '{print $2}')
#or
#PID=$(ps -e | grep vim | awk '{print $1}')
gdb --pid $PID
This assumes that the original process can be safely run in the background.