Continue linux shell script execution after command completion - linux

I need to perform a md5sum check after downloading a zip file. My shell script looks like this:
wget $1 -O "$5.zip"
md5pkg=md5sum $5.zip
#perform check and other operations with md5pkg
Now, the check is performed before download completion, resulting to an error since the .zip file hasn't bean downloaded yet.
What's the best approach to solve this problem?
thanks in advance.

If there is an ampersand in the value of $1, it will be parsed as the background operator, allowing the rest of your script to proceed. Quote it:
wget "$1" -O "$5.zip"
md5pkg=$( md5sum "$5.zip" )
In this case, I would expect the portion after the ampersand to be an invalid shell command and cause an error, which you don't mention. There may be other problems, but you should quote your variables in any case.

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.

Silence output in bash script using function advisable?

Suppose, someone is writing bash script in which it is needed to silent stdout,stderr and provide custom output.
Is it advisable to use function like below:
dump(){
"$#" > /dev/null 2>&1
}
And, then
dump rm filename || echo "custom-message"
What are the possible cases where it fails to function as expected?
This is a good technique. I use something like it all the time. Pros:
Preserves the exit code of the command.
Hides output of almost every program unless they directly write to /dev/tty or /dev/console, which is rare and probably for good reason anyways.
Works on shell builtins just as well as binaries. You can use this for cd, pushd/popd, etc.
Doesn't stop the command from reading from stdin. dump can be used at the end of a pipeline if you wish.
"$#" properly handles command names and arguments with whitespace, globs, and other special characters.
It looks good to me!
The only nitpick I have is that the name dump isn't the clearest.

How to modify a file name within a shell script?

I am writing a shell script to sync to a github repo, kick off the build, then take the output file, rename it, and move it to a location where it can be seen by Apache.
It's the renaming of the file that I've got not the faintest how to do within a shell script (I have virtually no experience with shell scripts - my understanding
Compiler will create /var/espbuild/firstpart_1vXX_secondpart.bin
I need to move this file to:
/var/www/html/builds/espbuild/firstpart_1vXX_DATE_secondpart_postfix.bin
1vXX is the version number
DATE is the output of date +%m-%d
postfix is just a string.
I'm not really certain where to start for something like this - I'm sure there's a graceful way, since this is the kind of thing shell scripts are made for, but I know just about nothing about shell scripts.
Thanks in advance
You can get the result of a command into a variable by using $():
DATE=$(date +%m-%d)
Then just use it in the new filename:
INPUT=/var/espbuild/firstpart_1vXX_secondpart.bin
OUTPUT=/var/www/html/builds/espbuild/firstpart_1vXX_${DATE}_secondpart_postfix.bin
mv ${INPUT} ${OUTPUT}
Edit: To get out the version part, here's a quick example:
VERSION=$(grep -o 1v.. <<< ${INPUT})
Then OUTPUT should be set like:
OUTPUT=/var/www/html/builds/espbuild/firstpart_${VERSION}_${DATE}_secondpart_postfix.bin
You can use this in BASH:
f='/var/espbuild/firstpart_1vXX_secondpart.bin'
s="${f##*/}"
s2=${s##*_}
dest="/var/www/html/builds/espbuild/${s%_*}_$(date '+%m-%d')_${s2%.*}_postfix.bin"
echo "$dest"
/var/www/html/builds/espbuild/firstpart_1vXX_07-14_secondpart_postfix.bin
cp "$f" "$dest"

Linux commands (cp, rm) are not executed in a perl script. Some works. But no error are returned

I'm having a very strange error.
I run a perl script which executes linux commands. They are executed like this:
my $err = `cp -r $HTML /tssobe/www/tstweb/$subpath/$HTMLDIR1`;
myLog("$err");
And $err is empty, which mean the command didn't return and error. (right?)
I tried to execute the linux command with exec "" or system (), but no success.
I tried to change the path. Same.
Also, I tried to run only the cp command in a new perl script. It works.
But not in my full perl script.
In this perl script, some commands are working, some are not.
The script was working yesterday, Not anymore this morning. No changes have been made in the meantime.
I tried a lot of things, I would be glad if anybody has an idea.
EDIT:
The server was having a lot of processes unterminated. Cleaning those solved the problem.
So the problem is related to another application, but I'll improve the logging thanks to your comments.
Small problem: you are NOT capturing STDERR, so you won't see the error (you are also not checking $? return code).
You should do
my $err = `cp -r $HTML /tssobe/www/tstweb/$subpath/$HTMLDIR1 2>&1`;
to redirect STDERR to STDOUT, or use one of the modules for running commands.
Large problem:
You should not run system commands from Perl for which Perl-native modules exist. In this case: File::Copy::Recursive module.
You can also roll your own directory copied from File::Copy.
Are you using backticks? Add -v to the cp commmand to see something in STDOUT and redirect the STDERR to STDOUT and check the cmd exitcode not the error message in the STDERR.
What about printing out the command output right after the execution?
my $err = `cp -rv $HTML /tssobe/www/tstweb/$subpath/$HTMLDIR1 2>&1`;
my $exitcode = $? >> 8;
warn "Output: $err\nexitcode: $exitcode\n";
It would be better to use qx. Check this: http://www.perlmonks.org/?node_id=454715
You may also want to quote arguments that may potentially contain any shell special characters, including spaces. As shell will do word splitting on the string given to it, if $HTML contains a space cp would get more arguments than you expect. Perl has very simple mechanism for that: \Q\E. Here is how you do it:
my $err = `cp -r \Q$HTML\E \Q/tssobe/www/tstweb/$subpath/$HTMLDIR1\E 2>&1`;
Anything except for alphanumeric will be backslash escaped before passing it to the shell. And you would provide exactly 2 arguments to cp regardless of what is in those variables.

Edit shell script while it's running

Can you edit a shell script while it's running and have the changes affect the running script?
I'm curious about the specific case of a csh script I have that batch runs a bunch of different build flavors and runs all night. If something occurs to me mid operation, I'd like to go in and add additional commands, or comment out un-executed ones.
If not possible, is there any shell or batch-mechanism that would allow me to do this?
Of course I've tried it, but it will be hours before I see if it worked or not, and I'm curious about what's happening or not happening behind the scenes.
It does affect, at least bash in my environment, but in very unpleasant way. See these codes. First a.sh:
#!/bin/sh
echo "First echo"
read y
echo "$y"
echo "That's all."
b.sh:
#!/bin/sh
echo "First echo"
read y
echo "Inserted"
echo "$y"
# echo "That's all."
Do
$ cp a.sh run.sh
$ ./run.sh
$ # open another terminal
$ cp b.sh run.sh # while 'read' is in effect
$ # Then type "hello."
In my case, the output is always:
hello
hello
That's all.
That's all.
(Of course it's far better to automate it, but the above example is readable.)
[edit] This is unpredictable, thus dangerous. The best workaround is , as described here put all in a brace, and before the closing brace, put "exit". Read the linked answer well to avoid pitfalls.
[added] The exact behavior depends on one extra newline, and perhaps also on your Unix flavor, filesystem, etc. If you simply want to see some influences, simply add "echo foo/bar" to b.sh before and/or after the "read" line.
Try this... create a file called bash-is-odd.sh:
#!/bin/bash
echo "echo yes i do odd things" >> bash-is-odd.sh
That demonstrates that bash is, indeed, interpreting the script "as you go". Indeed, editing a long-running script has unpredictable results, inserting random characters etc. Why? Because bash reads from the last byte position, so editing shifts the location of the current character being read.
Bash is, in a word, very, very unsafe because of this "feature". svn and rsync when used with bash scripts are particularly troubling, because by default they "merge" the results... editing in place. rsync has a mode that fixes this. svn and git do not.
I present a solution. Create a file called /bin/bashx:
#!/bin/bash
source "$1"
Now use #!/bin/bashx on your scripts and always run them with bashx instead of bash. This fixes the issue - you can safely rsync your scripts.
Alternative (in-line) solution proposed/tested by #AF7:
{
# your script
exit $?
}
Curly braces protect against edits, and exit protects against appends. Of course, we'd all be much better off if bash came with an option, like -w (whole file), or something that did this.
Break your script into functions, and each time a function is called you source it from a separate file. Then you could edit the files at any time and your running script will pick up the changes next time it gets sourced.
foo() {
source foo.sh
}
foo
Good question!
Hope this simple script helps
#!/bin/sh
echo "Waiting..."
echo "echo \"Success! Edits to a .sh while it executes do affect the executing script! I added this line to myself during execution\" " >> ${0}
sleep 5
echo "When I was run, this was the last line"
It does seem under linux that changes made to an executing .sh are enacted by the executing script, if you can type fast enough!
An interesting side note - if you are running a Python script it does not change. (This is probably blatantly obvious to anyone who understands how shell runs Python scripts, but thought it might be a useful reminder for someone looking for this functionality.)
I created:
#!/usr/bin/env python3
import time
print('Starts')
time.sleep(10)
print('Finishes unchanged')
Then in another shell, while this is sleeping, edit the last line. When this completes it displays the unaltered line, presumably because it is running a .pyc? Same happens on Ubuntu and macOS.
I don't have csh installed, but
#!/bin/sh
echo Waiting...
sleep 60
echo Change didn't happen
Run that, quickly edit the last line to read
echo Change happened
Output is
Waiting...
/home/dave/tmp/change.sh: 4: Syntax error: Unterminated quoted string
Hrmph.
I guess edits to the shell scripts don't take effect until they're rerun.
If this is all in a single script, then no it will not work. However, if you set it up as a driver script calling sub-scripts, then you might be able to change a sub-script before it's called, or before it's called again if you're looping, and in that case I believe those changes would be reflected in the execution.
I'm hearing no... but what about with some indirection:
BatchRunner.sh
Command1.sh
Command2.sh
Command1.sh
runSomething
Command2.sh
runSomethingElse
Then you should be able to edit the contents of each command file before BatchRunner gets to it right?
OR
A cleaner version would have BatchRunner look to a single file where it would consecutively run one line at a time. Then you should be able to edit this second file while the first is running right?
Use Zsh instead for your scripting.
AFAICT, Zsh does not exhibit this frustrating behavior.
usually, it uncommon to edit your script while its running. All you have to do is to put in control check for your operations. Use if/else statements to check for conditions. If something fail, then do this, else do that. That's the way to go.
Scripts don't work that way; the executing copy is independent from the source file that you are editing. Next time the script is run, it will be based on the most recently saved version of the source file.
It might be wise to break out this script into multiple files, and run them individually. This will reduce the execution time to failure. (ie, split the batch into one build flavor scripts, running each one individually to see which one is causing the trouble).

Resources