bash: can't output: ps -ef|grep to variable [duplicate] - linux

This question already has answers here:
Prevent bash from interpreting without quoting everything
(3 answers)
Closed 7 years ago.
I'm trying to insert the result of a command to variable using a Bash script on a Unix platform.
I need to use the next command: ps -ef|grep <my process>, but when I run it I got an error:
-bash: line 1: 5420: command not found
This is my code:
#!/bin/bash
ps_result=$(ps -ef|grep exe)
echo "$ps_result"
I thing this is some syntax problem because the 'grep' command is colored in yellow, but I can't to fix it (I tried using back-quotes `…` but it didn't work either).
edited:
There is the debug output relevant for the part of the code:
+ ssh ######
++ ps -ef
++ grep exe
Pseudo-terminal will not be allocated because stdin is not a terminal.
Warning: Permanently added '#####' (RSA) to the list of known hosts.
-bash: line 1: 5447: command not found
Here is the full code:
#!/bin/bash
ssh ##### << EOF
ps_result=$(ps -ef|grep exe)
echo "$ps_result"
EOF
I need the ps_result in order to do manipulations in it later.
* the ##### is edit here and not in the original code...

Preliminary versions of the question
The initial version of the question had the code:
ps_result= $(ps -ef|grep exe)
echo $ps_result
This has a space after the assignment, which causes trouble.
Preliminary answer
No spaces around assignments in shell:
ps_result=$(ps -ef|grep exe)
echo "$ps_result"
What you had ps_result= $(ps -ef|grep exe) runs ps | grep, and captures the output. It uses the first word of the output as a command name (nikitag, it seems) and runs that with the rest of the words in the output as arguments and with the environment variable ps_result set to an empty string. And since you don't have a command called nikitag, it fails.
Be very, very, very careful with spaces in shell scripts.
After update to include ssh and here document
Now that the question has been updated to show what's really going on, it is relatively simple to explain the problem. There's a here document and ssh involved too:
#!/bin/bash
ssh ##### << EOF
ps_result=$(ps -ef|grep exe)
echo "$ps_result"
EOF
Because the EOF is not quoted, the ps command is executed on the local machine before the ssh is started, and the output of the command is put into the here document. You need to quote the EOF at the start of the here document so that the document is transferred verbatim and executed on the remote machine:
#!/bin/bash
ssh ##### << 'EOF'
ps_result=$(ps -ef|grep exe)
echo "$ps_result"
EOF

Like the trace shows, the problem is not in the code you posted, but in the code which calls this code.
A here document without quoting will interpolate the process substitutions locally before passing the script to ssh. Use single quotes around the here document terminator after << to fix that.
#!/bin/bash
ssh ##### <<'EOF' # Notice quotes here
ps_result=$(ps -ef|grep exe)
echo "$ps_result"
EOF

The code should be no need of spaces
#!/bin/bash
ps_result=`ps -ef|grep exe`
echo $ps_result

Related

How to execute commands read from the txt file using shell? [duplicate]

This question already has answers here:
Run bash commands from txt file
(4 answers)
Closed 4 years ago.
I tried to execute commands read it from txt file. But only 1st command is executing, after that script is terminated. My script file name is shellEx.sh is follows:
echo "pwd" > temp.txt
echo "ls" >> temp.txt
exec < temp.txt
while read line
do
exec $line
done
echo "printed"
if I keep echo in the place of exec, just it prints both pwd and ls. But i want to execute pwd and ls one by one.
o/p am getting is:
$ bash shellEx.sh
/c/Users/Aditya Gudipati/Desktop
But after pwd, ls also need to execute for me.
Anyone can please give better solution for this?
exec in bash is meant in the Unix sense where it means "stop running this program and start running another instead". This is why your script exits.
If you want to execute line as a shell command, you can use:
line="find . | wc -l"
eval "$line"
($line by itself will not allow using pipes, quotes, expansions or other shell syntax)
To execute the entire file including multiline commands, use one of:
source ./myfile # keep variables, allow exiting script
bash myfile # discard variables, limit exit to myfile
A file with one valid command per line is itself a shell script. Just use the . command to execute it in the current shell.
$ echo "pwd" > temp.txt
$ echo "ls" >> temp.txt
$ . temp.txt

Check if script was started by another script [duplicate]

Let's assume I have 3 shell scripts:
script_1.sh
#!/bin/bash
./script_3.sh
script_2.sh
#!/bin/bash
./script_3.sh
the problem is that in script_3.sh I want to know the name of the caller script.
so that I can respond differently to each caller I support
please don't assume I'm asking about $0 cause $0 will echo script_3 every time no matter who is the caller
here is an example input with expected output
./script_1.sh should echo script_1
./script_2.sh should echo script_2
./script_3.sh should echo user_name or root or anything to distinguish between the 3 cases?
Is that possible? and if possible, how can it be done?
this is going to be added to a rm modified script... so when I call rm it do something and when git or any other CLI tool use rm it is not affected by the modification
Based on #user3100381's answer, here's a much simpler command to get the same thing which I believe should be fairly portable:
PARENT_COMMAND=$(ps -o comm= $PPID)
Replace comm= with args= to get the full command line (command + arguments). The = alone is used to suppress the headers.
See: http://pubs.opengroup.org/onlinepubs/009604499/utilities/ps.html
In case you are sourceing instead of calling/executing the script there is no new process forked and thus the solutions with ps won't work reliably.
Use bash built-in caller in that case.
$ cat h.sh
#! /bin/bash
function warn_me() {
echo "$#"
caller
}
$
$ cat g.sh
#!/bin/bash
source h.sh
warn_me "Error: You did not do something"
$
$ . g.sh
Error: You did not do something
g.sh
$
Source
The $PPID variable holds the parent process ID. So you could parse the output from ps to get the command.
#!/bin/bash
PARENT_COMMAND=$(ps $PPID | tail -n 1 | awk "{print \$5}")
Based on #J.L.answer, with more in depth explanations, that works for linux :
cat /proc/$PPID/comm
gives you the name of the command of the parent pid
If you prefer the command with all options, then :
cat /proc/$PPID/cmdline
explanations :
$PPID is defined by the shell, it's the pid of the parent processes
in /proc/, you have some dirs with the pid of each process (linux). Then, if you cat /proc/$PPID/comm, you echo the command name of the PID
Check man proc
Couple of useful files things kept in /proc/$PPID here
/proc/*some_process_id*/exe A symlink to the last executed command under *some_process_id*
/proc/*some_process_id*/cmdline A file containing the last executed command under *some_process_id* and null-byte separated arguments
So a slight simplification.
sed 's/\x0/ /g' "/proc/$PPID/cmdline"
If you have /proc:
$(cat /proc/$PPID/comm)
Declare this:
PARENT_NAME=`ps -ocomm --no-header $PPID`
Thus you'll get a nice variable $PARENT_NAME that holds the parent's name.
You can simply use the command below to avoid calling cut/awk/sed:
ps --no-headers -o command $PPID
If you only want the parent and none of the subsequent processes, you can use:
ps --no-headers -o command $PPID | cut -d' ' -f1
You could pass in a variable to script_3.sh to determine how to respond...
script_1.sh
#!/bin/bash
./script_3.sh script1
script_2.sh
#!/bin/bash
./script_3.sh script2
script_3.sh
#!/bin/bash
if [ $1 == 'script1' ] ; then
echo "we were called from script1!"
elsif [ $1 == 'script2' ] ; then
echo "we were called from script2!"
fi

Assigning variables inside remote shell script execution over SSH [duplicate]

This question already has answers here:
is it possible to use variables in remote ssh command?
(2 answers)
Closed 6 years ago.
I am trying to execute some shell script on a remote server via SSH.
Given below is a code sample:
ssh -i $KEYFILE_PATH ubuntu#$TARGET_INSTANCE_IP "bash" << EOF
#!/bin/bash
cat /home/ubuntu/temp.txt
string=$(cat /home/ubuntu/temp.txt )
echo $string
EOF
cat prints the expected result but
$string prints nothing.
How do I store the return value of cat in a variable?
You need to make the content of Here doc literal otherwise they will be expanded in the current shell, not in the desired remote shell.
Quote EOF:
ssh .... <<'EOF'
...
...
EOF
You should be able to simply do this:
ssh -i $KEYFILE_PATH ubuntu#$TARGET_INSTANCE_IP "bash" <<'_END_'
cat /home/ubuntu/temp.txt
string=$(cat /home/ubuntu/temp.txt)
echo $string
_END_
<<'_END_' ... _END_ is called a Here Document literal, or "heredoc" literal. The single quotes around '_END_' prevent the local shell from interpreting variables and commands inside the heredoc.
The intermediate shell is not required (assuming you use bash on the remote system).
Also you dont have to use an intermediate HERE-DOC. Just pass a multiline Command:
ssh -i $KEYFILE_PATH ubuntu#$TARGET_INSTANCE_IP '
cat /home/ubuntu/temp.txt
string=$(cat /home/ubuntu/temp.txt )
echo $string
'
Note I am using single quotes to prevent evaluation on the local shell.

Bash command substitution on remote host [duplicate]

This question already has answers here:
How to cat <<EOF >> a file containing code?
(5 answers)
Closed 7 years ago.
I'm trying to run a bash script that ssh's onto a remote host and stops the single docker container that is running.
#!/usr/bin/env bash
set -e
ssh <machine> <<EOF
container=$(docker ps | awk 'NR==2' | awk '{print $1;}')
docker stop $container
EOF
However, I get the following error:
stop.sh: line 4: docker: command not found
When I do this manually (ssh to the machine, run the commands) all is fine, but when trying to do so by means of a script I get the error. I guess that my command substitution syntax is incorrect and I've searched and tried all kinds of quotes etc but to no avail.
Can anyone point me to where I'm going wrong?
Use <<'EOF' (or <<\EOF -- quoting only the first character will have the same effect) when starting your heredoc to prevent its expansions from being evaluated locally.
BTW, personally, I'd write this a bit differently:
#!/bin/sh -e
ssh "$1" bash <<'EOF'
{ read; read container _; } < <(docker ps)
docker stop "$container"
EOF
The first read consumes the first line of docker ps output; the second extracts only the first column -- using bash builtins only.

How do I know if I'm running a nested shell?

When using a *nix shell (usually bash), I often spawn a sub-shell with which I can take care of a small task (usually in another directory), then exit out of to resume the session of the parent shell.
Once in a while, I'll lose track of whether I'm running a nested shell, or in my top-level shell, and I'll accidentally spawn an additional sub-shell or exit out of the top-level shell by mistake.
Is there a simple way to determine whether I'm running in a nested shell? Or am I going about my problem (by spawning sub-shells) in a completely wrong way?
The $SHLVL variable tracks your shell nesting level:
$ echo $SHLVL
1
$ bash
$ echo $SHLVL
2
$ exit
$ echo $SHLVL
1
As an alternative to spawning sub-shells you could push and pop directories from the stack and stay in the same shell:
[root#localhost /old/dir]# pushd /new/dir
/new/dir /old/dir
[root#localhost /new/dir]# popd
/old/dir
[root#localhost /old/dir]#
Here is a simplified version of part of my prompt:
PS1='$(((SHLVL>1))&&echo $SHLVL)\$ '
If I'm not in a nested shell, it doesn't add anything extra, but it shows the depth if I'm in any level of nesting.
Look at $0: if it starts with a minus -, you're in the login shell.
pstree -s $$ is quite useful to see your depth.
The environment variable $SHLVL contains the shell "depth".
echo $SHLVL
The shell depth can also be determined using pstree (version 23 and above):
pstree -s $$ | grep sh- -o | wc -l
I've found the second way to be more robust than the first whose value was reset when using sudo or became unreliable with env -i.
None of them can correctly deal with su.
The information can be made available in your prompt:
PS1='\u#\h/${SHLVL} \w \$ '
PS1='\u#\h/$(pstree -s $$ | grep sh- -o | tail +2 | wc -l) \w \$ '
The | tail +2 is there to remove one line from the grep output. Since we are using a pipeline inside a "$(...)" command substitution, the shell needs to invoke a sub-shell, so pstree report it and grep detects one more sh- level.
In debian-based distributions, pstree is part of the package psmisc. It might not be installed by default on non-desktop distributions.
As #John Kugelman says, echo $SHLVL will tell you the bash shell depth.
And as #Dennis Williamson shows, you can edit your prompt via the PS1 variable to get it to print this value.
I prefer that it always prints the shell depth value, so here's what I've done: edit your "~/.bashrc" file:
gedit ~/.bashrc
and add the following line to the end:
export PS1='\$SHLVL'":$SHLVL\n$PS1"
Now you will always see a printout of your current bash level just above your prompt. Ex: here you can see I am at a bash level (depth) of 2, as indicated by the $SHLVL:2:
$SHLVL:2
7510-gabriels ~ $
Now, watch the prompt as I go down into some bash levels via the bash command, then come back up via exit. Here you see my commands and prompt (response), starting at level 2 and going down to 5, then coming back up to level 2:
$SHLVL:2
7510-gabriels ~ $ bash
$SHLVL:3
7510-gabriels ~ $ bash
$SHLVL:4
7510-gabriels ~ $ bash
$SHLVL:5
7510-gabriels ~ $ exit
exit
$SHLVL:4
7510-gabriels ~ $ exit
exit
$SHLVL:3
7510-gabriels ~ $ exit
exit
$SHLVL:2
7510-gabriels ~ $
Bonus: always show in your terminal your current git branch you are on too!
Make your prompt also show you your git branch you are working on by using the following in your "~/.bashrc" file instead:
git_show_branch() {
__gsb_BRANCH=$(git symbolic-ref -q --short HEAD 2>/dev/null)
if [ -n "$__gsb_BRANCH" ]; then
echo "$__gsb_BRANCH"
fi
}
export PS1="\e[7m\$(git_show_branch)\e[m\n\h \w $ "
export PS1='\$SHLVL'":$SHLVL $PS1"
Source: I have no idea where git_show_branch() originally comes from, but I got it from Jason McMullan on 5 Apr. 2018. I then added the $SHLVL part shown above just last week.
Sample output:
$SHLVL:2 master
7510-gabriels ~/GS/dev/temp $
And here's a screenshot showing it in all its glory. Notice the git branch name, master, highlighted in white!
Update to the Bonus section
I've improved it again and put my ~/.bashrc file on github here. Here's a sample output of the new terminal prompt. Notice how it shows the shell level as 1, and it shows the branch name of the currently-checked-out branch (master in this case) whenever I'm inside a local git repo!:
Cross-referenced:
Output of git branch in tree like fashion
ptree $$ will also show you how many levels deep you are
If you running inside sub-shell following code will yield 2:
ps | fgrep bash | wc -l
Otherwise, it will yield 1.
EDIT Ok, it's not so robust approach as was pointed out in comments :)
Another thing to try is
ps -ef | awk '{print $2, " ", $8;}' | fgrep $PPID
will yield 'bash' if you in sub-shell.

Resources