How to denote that pipes on a remote machine end, and a pipe on the local machine follows - bash

I want to get some files from a remote machine. For this I make some pipes to determine which files have to be fetched, and these results I want to put in a pipe also.
The remote pipes combine to 1 command which is given to ssh.
I do not know how to let know where the pipes on the remote machine end and to put the results in the new local pipe. So I do:
ssh user#remote find ... | grep ...| awk ...| ls
The first 2 pipes are remote (find, grep , awk run on the remote machine), and the last pipe is local (ls runs on the local machine).

Wrap the part of the command you want to executed on remote machine into double quotes. E.g. find, grep and awk will be executed remote, while less will be exceuted local.
ssh user#remote "find ... | grep ...| awk ... "| less
As "tripleee" added in the comments it's better to use single quotes if there is no variable substitution in the quoted string. So use " if there is a variable inside the remote command.
ssh user#remote "find $foo | grep ...| awk ... "| less
or use ' if there is no variable involved.
ssh user#remote 'find "foo" | grep ...| awk ... '| less

Related

'grep' not recognized over ssh

I'm trying to close my app remotely like that:
ssh pi#192.168.0.227 "kill $(ps aux | grep '[M]yApp' | awk '{print $2}')"
It fails and prompts:
grep : The term 'grep' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
If I login via SSH first and then do the command, it works, but I need it to be one-liner. I've set /etc/ssh/sshd_config variable PermitUserEnvironment to yes, and tried to use full path to grep (/bin/grep), and even removed spaces around the pipe (these were all answers to questions similar to mine) but nothing allows me to pass the command. What am I missing?
The string is expanded by your local shell before being passed to the other host. Since it is a double-quoted string the command within $() runs on your local host. The easiest way to pass such a command to a remote host is with a "quoted" here document:
ssh pi#192.168.0.227 <<'EOF'
kill $(ps aux | grep '[M]yApp' | awk '{print $2}')"
EOF
Similar: How have both local and remote variable inside an SSH command

nslookup/dig/drill commands on a file that contains websites to add ip addresses

UPDATE : Still open for solutions using nslookup without parallel, dig or drill
I need to write a script that scans a file containing web page addresses on each line, and adds to these lines the IP address corresponding to the name using nslookup command. The script looks like this at the moment :
#!/usr/bin/
while read ip
do
nslookup "$ip" |
awk '/Name:/{val=$NF;flag=1;next} /Address:/ &&
flag{print val,$NF;val=""}' |
sed -n 'p;n'
done < is8.input
The input file contains the following websites :
www.edu.ro
vega.unitbv.ro
www.wikipedia.org
The final output should look like :
www.edu.ro 193.169.21.181
vega.unitbv.ro 193.254.231.35
www.wikipedia.org 91.198.174.192
The main problem i have with the current state of the script is that it takes the names from nslookup (which is good for www.edu.ro) instead of taking the aliases when those are available. My output looks like this:
www.edu.ro 193.169.21.181
etc.unitbv.ro 193.254.231.35
dyna.wikimedia.org 91.198.174.192
I was thinking about implementing a if-else for aliases but i don't know how to do one on the current command. Also the script can be changed if anyone has a better understanding of how to format nslookup to show it like the output given.
Minimalist workaround quasi-answer. Here's a one-liner replacement for the script using GNU parallel, host (less work to parse than nslookup), and sed:
parallel "host {} 2> /dev/null |
sed -n '/ has address /{s/.* /'{}' /p;q}'" < is8.input
...or using nslookup at the cost of added GNU sed complexity.
parallel "nslookup {} 2> /dev/null |
sed -n '/^A/{s/.* /'{}' /;T;p;q;}'" < is8.input
...or using xargs:
xargs -I '{}' sh -c \
"nslookup {} 2> /dev/null |
sed -n '/^A/{s/.* /'{}' /;T;p;q;}'" < is8.input
Output of any of those:
www.edu.ro 193.169.21.181
vega.unitbv.ro 193.254.231.35
www.wikipedia.org 208.80.154.224
Replace your complete nslookup line with:
echo "$IP $(dig +short "$IP" | grep -m 1 -E '^[0-9.]{7,15}$')"
This might work for you (GNU sed and host):
sed '/\S/{s#.*#host & | sed -n "/ has address/{s///p;q}"#e}' file
For all non-empty lines: invoke the host command on the supplied host name and pipe the results to another invocation of sed which strips out text and quits after the first result.

Global Variables using Quoted vs. Unquoted Heredocs

I'm curious if I can have my cake and eat it too. I'm writing a script that needs to find the directory with the most recent date on a remote server. I then need to build that path so I can find specific .csv files on the server.
The script takes an input called folder and it needs to be appended to the end of the path. I've noticed I can pass folder into the heredoc and have it expanded, but then I lose out on the awk expansion I need to do. Here is an example:
folder='HBEP'
ssh $server /bin/bash << EOF
ls -t /projects/bison/git |
head -1 |
awk -v folder=$folder '{print "projects/bison/git/"$1"/assessment/LWR/validation/"folder}'
EOF
This produces a close but wrong output:
# output:
/projects/bison/git//assessment/LWR/validation/HBEB
# should be:
/projects/bison/git/bison_20190827/LWR/validation/HBEP
Now, when I quote EOF, I can access the piped in variable but not the folder variable:
folder='
ssh $server /bin/bash << 'EOF'
ls -t /projects/bison/git |
head -1 |
awk -v folder="$folder" '{print "projects/bison/git/"$1"/assessment/LWR/validation/"folder}'
EOF
# output:
projects/bison/git/bison_20190826/assessment/LWR/validation/
# should be:
projects/bison/git/bison_20190826/assessment/LWR/validation/HBEP
Is there a way I can leverage expansion in the heredoc and the outside shell?
You can use the unquoted version of heredoc. Just add the \ before $ if you want to avoid the parameter expansion.
eg
folder='HBEP'
ssh $server /bin/bash << EOF
ls -t /projects/bison/git |
head -1 |
awk -v folder=$folder '{print "projects/bison/git/"\$1"/assessment/LWR/validation/"folder}'
EOF

Bash script: How to remote to a computer run a command and have output pipe to another computer?

I need to create a Bash Script that will be able to ssh into a computer or Machine B, run a command and have the output piped back to a .txt file on machine A how do I go about doing this? Ultimately it will be list of computers that I will ssh to and run a command but all of the output will append to the same .txt file on Machine A.
UPDATE: Ok so I went and followed what That other Guy suggested and this is what seems to work:
File=/library/logs/file.txt
ssh -n username#<ip> "$(< testscript.sh)" > $File
What I need to do now is instead of manually entering an ip address, I need to have it read from a list of hostnames coming from a .txt file and have it place it in a variable that will substitute the ip address. An example would be: ssh username#Variable in which "Variable" will be changing each time a word is read from a file containing hostnames. Any ideas how to go about this?
This should do it
ssh userB#machineB "some command" | ssh userA#machineA "cat - >> file.txt"
With your commands:
ssh userB#machineB <<'END' | ssh userA#machineA "cat - >> file.txt"
echo Hostname=$(hostname) LastChecked=$(date)
ls -l /applications/utilities/Disk\ Utility.app/contents/Plugins/*Partition.dumodule* | awk '{printf "Username=%s DateModified=%s %s %s\n", $3, $6, $7, $8}'
END
You could replace the ls -l | awk pipeline with a single stat call, but it appears that the OSX stat does not have a way to return the user name, only the user id

execute grep regular expression involving `|` using ssh on remote host

I am trying to run grep command involving regular expression '|' on a server using ssh.
ssh rpatil#192.168.1.5 grep -E "GapEvent|GapFilled" "$logFile" > $server-$testName.log
now '|' in the command is being treated as pipe and error "no command GapFilled" is being raised.
I tried 'GapEvent|GapFilled' or '(GapEvent|GapFilled)'
so how should regular expression "GapEvent|GapFilled" should be written so that | is not treated as pipe?
You need two levels of quotes since the command line is evaluated twice (once locally when ssh is executed and once when grep is executed on the remote side). You can use one of these patterns:
"'a|b'"
'"a|b"'
"\"a|b\""
Escape the | like this \|
grep -E "GapEvent\|GapFilled" "$logFile" file
Simply use two expressions:
grep -E -e "GapEvent" -e "GapFilled" "$logFile"
-E may no longer be needed here. -F may also be a preference.

Resources