gitlab-ci: remote shell execution with variable expansion - bash

Within a gitlab-ci job, I want to create a directory (on a remote server) with the job id and perform some actions more or less as follows:
- ssh user#server mkdir -p /user/$CI_JOB_ID
- ssh user#server <perform_some_action_that_creates_several_zip_files>
- LAST_MODIFIED_FILE=$(ssh user#server bash -c 'find /user/$CI_JOB_ID -iname "*.zip" | tail -n 1 | xargs readlink -f')
The directory does get created and the action that creates several zips works out.
However, the last command that I use for getting the last modified/created .zip does not work, because $CI_JOB_ID does not seem to get expanded.
Any suggestions?

This issue is due to your ssh call. The way you do it now, you are mixing contexts :
ssh user#server bash -c 'find /user/$CI_JOB_ID -iname "*.zip" | tail -n 1 | xargs readlink -f'
bash -c 'my_commands' : you use simple quotes, so ssh will execute exactly the instruction my_commands. In your case, it will try to find the remote value for $CI_JOB_ID, instead of using the local one.
ssh user#server bash -c "my_commands" : withssh, the commands to execute on the remote shell are sent as a single string. Thus, if you were to run ssh with this quotation, it would try to run "bash -c find ... | tail ... | xargs ...". Here, only find is run through bash -c.
In your case, simply writing directly the following statement should do the trick :
ssh user#server "find /user/$CI_JOB_ID -iname '*.zip' | tail -n 1 | xargs readlink -f"
Otherwise, if you want to keep using the bash -c syntax, you'd have to escape the quotation so that it is propagated to the remote machine :
ssh user#server bash -c \"find /user/$CI_JOB_ID -iname '*.zip' | tail -n 1 | xargs readlink -f\"

Related

How to run a command like xargs on a grep output of a pipe of a previous xargs from a command in Bash

I'm trying to understand what's happening here out of curiosity, even though I can just copy and paste the output of the terminal to do what I need to do. The following command does not print anything.
ls /opt/local/var/macports/registry/portfiles -1 | sed 's/-.*//g' | sort -u | parallel "sudo port -N install" {} 2>&1 | grep -Po "Use '\K.*(?=')" | parallel "{}"
The directory I call ls on contains a bunch of filenames starting with the string I want to extract that ends at the first dash (so stringexample-4.2009 pipes stringexample into parallel (like xargs but to run each line separately). After running the command sudo port install <stringexample>, I get error outputs like so:
Unable to activate port <stringexample>. Use 'port -f activate <stringexample>' to force the activation.
Now, I wish to run port -f activate <stringexample>. However, I cannot seem to do anything with the output port -f activate gettext that I get to the terminal.
I cannot even do ... | grep -Po "Use '\K.*(?=')" | xargs echo or ... | grep -Po "Use '\K.*(?=')" >> commands_to_run.txt (the output stream to file only creates an empty file), despite the shorter part of the command:
ls /opt/local/var/macports/registry/portfiles -1 | sed 's/-.*//g' | sort -u | parallel "sudo port -N install {}" 2>&1 | grep -Po "Use '\K.*(?=')"
printing the commands to the terminal. Why does the pipe operator not work here? If the commands I wish to run are outputting to the terminal, surely there's got to be a way to capture them.

Expect Script scp latest file in directoy

I am trying to scp latest file from remote machine however getting below error;
can't read "(ssh root#1.1..... "ls -t /test/*txt | head
-1")": no such variable
my expect script ;
spawn scp -r root#$remote_ip:/test/$(ssh root#$remote_ip "ls -t /test/*txt | head -1") /mypath
how should I get latest file from remote machine with expect script?
$(...) are shell syntax. To perform the same functionality in Tcl/expect, use the exec command.
spawn scp -r root#$remote_ip:/test/[exec ssh root#$remote_ip "ls -t /test/*txt | head -1"] /mypath
It doesn't have to be a single line, for maintainability, split it
set latest [exec ssh root#$remote_ip "ls -t /test/*txt | head -1"]
spawn scp -r root#$remote_ip:/test/$latest /mypath
However, I suspect you're using expect to send the passwords, either:
spawn ssh root#$remote_ip "ls -t /test/*txt | head -1"
expect "password"
send "$passwd\r"
expect eof
# parse $expect_out(buffer) to extract the file
But, your life will be much easier if
you set up ssh key authentication and avoid expect altogether:
ssh-keygen
ssh-copy-id root#$remote_ip
latest=$(ssh root#$remote_ip "ls -t /test/*txt | head -1")
scp -r root#$remote_ip:/test/$latest /mypath
scp -r root#$remote_ip:ssh root#$remote_ip ls /test/* -1td | head -1 /mypath/.

Using shell expansion with Ansible

I'm trying to execute a remote command via Ansible which requires gathering the PID of the process:
ansible webserver -m shell -a 'jstack -l $(pgrep -f java)'
However it seems Ansible is not able to expand the shell command contained in parenthesis (tried as well with grave accent):
127.0.0.1 | FAILED | rc=1 >>
Usage:
jstack [-l] <pid>
Executing just the command in the expansion reveals that expansion does not take place:
ansible webserver -a 'echo $(pgrep -f java)'
192.168.0.1 | success | rc=0 >>
$(pgrep -f java)
You'll want to escape the $ dollar sign, like so:
ansible all -i inventories/prod/hosts -m shell -a "echo \$(pgrep -f java)"

bash - regex string from command line

I'm trying to do something like this:
wget -O - http://example.com/myfolder/myfile | ssh mypc 'cat > /tmp/myfile'
Note that 'myfile' is the name used in wget and cat commands. So, my question is if I can apply a regex to the http://example.com/myfolder/myfile in order to pass it to cat command removing all except for the name of the file (in this case m file). Long story short, I would like to have ONE input to give to both commands with help of regex for the second one, something like:
wget -O - http://example.com/myfolder/myfile | ssh mypc 'cat > /tmp/regex(http://example.com/myfolder/myfile, last_occurrence_of(/))'
Thanks for the help
You can use basename command:
url=http://example.com/myfolder/myfile; wget -O - $url | ssh mypc "cat > /tmp/$(basename $url)"
You can also use the bash operator ##:
url=http://example.com/myfolder/myfile; wget -O - $url | ssh mypc "cat > /tmp/${url##*/}"
In this case the regex is */.

Scape quotes on remote command

I'm try to pass a commadn on remote server.
Command work fine on local server, but when try pass on remote server trought ssh get error for bad scpaing
ls -t /root/mysql/*.sql | awk 'NR>2 {system(\"rm \"" $0 \"\"")}'
Full comnand
ssh root#host -p XXX "mysqldump --opt --all-databases > /root/mysql/$(date +%Y%m%d%H%M%S).sql;ls -t /root/mysql/*.sql | awk 'NR>2 {system(\"rm \"" $0 \"\"")}'"
Actually no need to use awk and avoid all that quotes escaping:
ls -t /root/mysql/*.sql | tail -n +1 | xargs rm
This is assuming your *.sql files don't have any whitespaces otherwise you should use stat command and sort the output using sort.

Resources