Can I execute nested or chained commands in UNIX shell? - linux

Can I execute command within another command in UNIX shells?
If impossible, can I use the output of the previous command as the input of next command, as in:
command x then command y,
where in command y I want use the output of command x?

You can use the backquotes for this.
For example this will cat the file.txt
cat `echo file.txt`
And this will print the date
echo the date is `date`
The code between back-quotes will be executed and be replaced by its result.

You can do something like;
x=$(grep $(dirname "$path") file)
here dirname "$path" will run first and its result will be substituted and then grep will run, searching for the result of dirname in the file

What exactly are you trying to do? It's not clear from the commands you are executing. Perhaps if you describe what you're looking for we can point you in the right direction. If you want to execute a command over a range of file (or directory) names returned by the "find" command, Colin is correct, you need to look at the "-exec" option of "find". If you're looking to execute a command over a bunch of arguments listed in a file or coming from stdin, you need to check out the "xargs" commands. If you want to put the output of a single command on to the command line of another command, then using "$(command)" (or 'command' [replace the ' with a backquote]) will do the job. There's a lot of ways to do this, but without knowing what it is you're trying it's hard to be more helpful.

Here is an example where I have used nested system commands.
I had run "ls -ltr" on top of find command. And it executes
it serially on the find output.
ls -ltr $(find . -name "srvm.jar")

Related

Execute a command in a script and store the result in a file in bash

I'm trying to store the result of this command that is written in a script
ls -l /etc|wc -l
in a variable on another file.
To summarize, I have a script with that command and when I execute it, I want the result to be stored in a variable in another file.
Can someone help me with this please?
You may try to use temporary file (if possible).
This command:
ls -l /etc|wc -l > /tmp/myvar.txt
Another file:
myvar="$(cat /tmp/myvar.txt)"
You just need to use '> path/to/file' at the end of your command to redirect the output to a file (this will override the file content).
If you need another behavior, like append the content, you should use '>>' instead of '>'.
Take a look here for more details.
I'm not sure I understand what you're trying to do so I'll give you two solutions.
If the command you mention is in some file script_A.sh and you want the results of that script stored in some variable $var when running some other script script_B.sh, randomir's solution is good. In script_B:
var=$(bash path/to/script_A.sh)
If what you're asking is to run script_A.sh and then have it write a new line to a file that would store the results to a value when you run script_B.sh, I suppose you could run something like:
result=$(ls -l /etc|wc -l)
echo "var=\"$result\"" > path/to/script_B.sh
or even replace a line in a script_B.sh that already exists:
result= $(ls -l /etc|wc -l)
sed -i "s|var=SOMEPLACEHOLDER|var='$result'|" path/to/script_B.sh
If the latter is what you want, though, can you tell us more about what you're trying to accomplish? There's probably a better way than what you propose.

Running script on output files

I am trying to run script on output files that can be further used as input files for gaussian.
I wanted to know what are the commands used in Linux to run the script on .log files and .HSCP1 files.
Many thanks,
Regards,
The generic syntax of passing an argument to a script in linux, assuming your script is named script.sh and your target file is named arg.log, would be
script.sh arg.log
This assigns the name arg.log to $1 inside the environment of the executing copy of script.sh. If you don't then do something with that, it won't matter.
You might also have your script read its stdin like this:
script.sh < arg.log
which will put the contents os arg.log on script.sh's stdin, but unless it reads them accordingly, it won't matter.
Of course, both these assume script.sh is in your $PATH; otherwise you will need to apply a path for the OS, such as /path/to/dir/with/script.sh or (if you are in the same directory) ./script.sh.
If what you are asking is how to get a lorge number of files assigned as arguments, you could pass wildcards - for the first example above, that could be done as
./script.sh /path/to/*.log /also/to/other.*
or you could use find, maybe with xargs like so -
find /path/to/files/ -name *.log | xargs /path/of/script.sh
which will call the script over and over.
I hope one of these helps, but you really must provide more context for what you are doing and how.

How can I write a command to shell script from a shell script without evaluating the command

I'm struggling with passing a shell command. Specifically, I have written a shell file that the user will run. In it, another shell file is written based on the user inputs. This file will then be passed to a command that submits the job.
Now in this internal shell file I have a variable containing a function. However, when I run the user shell script I can't get this function to pass in a way that the internal shell file can use it.
I can't share my work but I'll try to make an example
#User shell script
cat >test.txt <<EOF
#a bunch of lines that are not relevant
var=`grep examples input.txt`
/bin/localfoo -${var}
EOF
# pass test.txt to localfoo2
/bin/localfoo2 /test.txt
When I run the 'User Shell Script' it prints that grep can't find the file, but I don't want grep to be evaluated. I need it to be written, as is, so that when the line '/bin/localfoo2 /test.txt' is read, grep is evaluated.
I've tried a number of things. I've tried double back ticks, i've tried using 'echo $(eval $var)'. But none of the methods I've found through googling have managed to pass this var in a way that will accomplish what I want.
Your help is much appreciated.
You can try with single quote (').
You have to put the single quote in before the grep command and end of the grep command like below.
#User shell script
cat >test.txt <<EOF
#a bunch of lines that are not relevant
var='`grep examples input.txt`'
/bin/localfoo -${var}
EOF
# pass test.txt to localfoo2
/bin/localfoo2 /test.txt
I did not understand where you have to execute that grep command.
If you want to execute the grep command inside the localfoo script, I hope this method will help.

Commands work from Shell script but not from command line?

I quickly searched for this before posting, but could not find any similar posts. Let me know if they exist.
The commands being executed seem very simple. A directory listing is used as the input for a function.
The directory contains a bunch of files named "epi1_mcf_0###.nii.gz"
Command-line version (bash is running when this is executed):
fslmerge -t output_file `ls epi1_mcf_0*.nii.gz`
Shell script version:
#!/bin/bash
fslmerge -t output_file `ls epi1_mcf_0*.nii.gz`
The command-line version fails, but the shell script one works perfectly.
The error message is specific to the function, but it's included anyway.
** ERROR (nifti_image_read): failed to find header file for 'epi1_mcf_0000.nii.gz'
** ERROR: nifti_image_open(epi1_mcf_0000.nii.gz): bad header info
Error: failed to open file epi1_mcf_0000.nii.gz
Cannot open volume epi1_mcf_0000.nii.gz for reading!
I have been very frustrated with this problem (less so after I figured out that there was a way to get the command to work).
Any help would be appreciated.
(Or is the general consensus that the problem should be looked for in the "fslmerge" function?)
`ls epi1_mcf_0*.nii.gz` is better written as simply epi1_mcf_0*.nii.gz. As in:
fslmerge -t output_file epi1_mcf_0*.nii.gz
The `ls` doesn't add anything.
Note: Posted as an answer instead of comment. The Markdown-lite comment parser choked on my `` `ls epi1_mcf_0*.nii.gz` `` markup.
(I mentioned this in a comment first, but I'll make an answer since it helped!)
Do you have any shell aliases defined? (Type alias) Those will affect commands typed at the command line, but not scripts.
Linux often has ls defined as ls --color. This may affect the output since the colour codes are sent as escape codes through the regular output stream. If you use ls --color=auto it will auto-detect whether its output is a terminal or not. From man ls:
By default, color is not used to distinguish types of files. That is
equivalent to using --color=none. Using the --color option without the
optional WHEN argument is equivalent to using --color=always. With
--color=auto, color codes are output only if standard output is connected to a terminal (tty).

Shell Script - Linux

I want to write a very simple script , which takes a process name , and return the tail of the last file name which contains the process name.
I wrote something like that :
#!/bin/sh
tail $(ls -t *"$1"*| head -1) -f
My question:
Do I need the first line?
Why isn't ls -t *"$1"*| head -1 | tail -f working?
Is there a better way to do it?
1: The first line is a so called she-bang, read the description here:
In computing, a shebang (also called a
hashbang, hashpling, pound bang, or
crunchbang) refers to the characters
"#!" when they are the first two
characters in an interpreter directive
as the first line of a text file. In a
Unix-like operating system, the
program loader takes the presence of
these two characters as an indication
that the file is a script, and tries
to execute that script using the
interpreter specified by the rest of
the first line in the file
2: tail can't take the filename from the stdin: It can either take the text on the stdin or a file as parameter. See the man page for this.
3: No better solution comes to my mind: Pay attention to filenames containing spaces: This does not work with your current solution, you need to add quotes around the $() block.
$1 contains the first argument, the process name is actually in $0. This however can contain the path, so you should use:
#!/bin/sh
tail $(ls -rt *"`basename $0`"*| head -1) -f
You also have to use ls -rt to get the oldest file first.
You can omit the shebang if you run the script from a shell, in that case the contents will be executed by your current shell instance. In many cases this will cause no problems, but it is still a bad practice.
Following on from #theomega's answer and #Idan's question in the comments, the she-bang is needed, among other things, because some UNIX / Linux systems have more than one command shell.
Each command shell has a different syntax, so the she-bang provides a way to specify which shell should be used to execute the script, even if you don't specify it in your run command by typing (for example)
./myscript.sh
instead of
/bin/sh ./myscript.sh
Note that the she-bang can also be used in scripts written in non-shell languages such as Perl; in the case you'd put
#!/usr/bin/perl
at the top of your script.

Resources