I need help with writing a bash script. I have very little knowledge on scripting, but am willing to learn.
I have a file with lines that looks like this: 1204200141345_song.mp3. The numbers are date and time like day=12, month=04, year=2014, hour=13, min=45
I need a script that will read through the file and if a line matches the current date and time it will play the mp3 via mplayer/mpg123 at the correct time. When the mp3 ends it must continue. The file changes all the time. New lines go in and other line are removed. The script will run as long as the pc is on.
I am not sure where to start, any advice is appreciated.
So, you willing to learn - some starting points...
The script will run as long as the pc is on.
Ok, this can be achieved with a loop what will run forever, like the next one:
while :
do
some_commands
done
When the mp3 ends it must continue.
As above - if the some_commands contains the command to playing the file - the script will wait until the command finishes and will start over - again, and again, and again... forever...
The file changes all the time. New lines go in and other line are
removed.
Don't need to care until it is a file...
... if a line matches
the current date and time it will play the mp3 via mplayer/mpg123 at
the correct time.
So, first you need get the date in the needed format. This can be achieved with the date command. (man date)
The needed format is %d%m%Y%H%M, so the
date +"%d%m%Y%H%M"
will print the current date/time in the wanted format
assing the result of the command to some variable in bash can be done with the
variable=$(command argumens)
so,
datestr=(date +"%d%m%Y%H%M")
echo $datestr
will assing the current date/time to variable datestr.
... read through the file and if a line matches the current date and time ...
The Read-thru and match can be done with the grep command (man grep), so, you need use
grep "what_want_to_match" filename_where_want_to_match.txt
The grep exits with a status codes
0 - found a match
1 - match not found
2 - the file doesn't exists
The exist status of previous command is stored in the special variable $?. You can test the exit status, for example with the bash's case construction, like:
some_command
case $? in
2) echo "The some_command exited with status 2" ; exit 2 ;;
1) echo "with status 1" ; do_something_else ;;
0) echo "normal exit" ;;
esac
Now, you have enough informations to try write the script yourself, and if you meet some specific error - ask again... ;)
Related
I want to print the date after every bash command I run.
This could help me understand how much a command took to execute when I am away from keyboard.
I know I could do
`DATE=`date +%d/%m/%Y\ %H:%M:%S` && echo $DATE`
to get the date but I don't know how or even if it could be possible to run this command after every command I execute on bash.
I would also be interested in running the same command before every command so I could know how long a command took.
Is it possible?
What file should I edit?
For example:
$ wget google.com
15/07/2017 23:40:05
I would be happy, if I could also introduce this following feauture:
$ wget google.com
15/07/2017 23:40:05 15/07/2017 23:40:11
Program run for 00:00:06
where the first date is when I ran the program, second is when program terminated the third is self-explanatonary.
As you understood, I don't want to type every time
$ wget google.com && `DATE=`date +%d/%m/%Y\ %H:%M:%S` && echo $DATE`
To execute a cmd before every command entered, set a trap on DEBUG. Eg.
trap date DEBUG
To execute that command before emitting a prompt, set PROMPT_COMMAND:
PROMPT_COMMAND=date
This does exactly that:
PROMPT_COMMAND+=$'\n'"date +%d/%m/%Y\ %H:%M:%S"
The string in PROMPT_COMMAND gets evaluated after every command. You just need to add the date command to whatever you already had in it. ($'\n' (newline) is a somewhat more robust joiner than ; as two consecutive ; would give you a syntax error)
You can add date/time to your prompt, via PS1 variable. You could use date command, but it's more efficient to use the supported special characters, like \d for date, or \D{strftime-fmt}.
For example:
PS1='\u#\h[\D{%F} \D{%T}]\w\$ '
or, with color:
PS1='\[\033[01;32m\]\u#\h\[\033[00m\][\[\033[02;33m\]\D{%F}\[\033[08m\]T\[\033[00m\]\[\033[02;33m\]\D{%T}\[\033[00m\]]\[\033[01;34m\]\w\[\033[00m\]\$ '
will show:
user#host[2017-07-16 00:01:17]~/somedir$
Note that in the second case (with color) we have a valid ISO8601 timestamp, with a "hidden" date/time separator T in the middle. If you select it with a mouse, T is visible and can be copied. (Also double-click will select the complete timestamp, not only date or time.)
To print timestamp after every command just modify your PS1 prompt and add date to it. The only catch here is that it will tell you time when command ended and new prompt showed. So in case you have your prompt open for long time just hit enter to capture start time before running your command.
PS1="\D{%F %T} \$ "
See this arch wiki page or just google bash prompt customization.
To add time spent executing program just add time before the command
$ time wget google.com
It will give you output like this
real 0m0.177s
user 0m0.156s
sys 0m0.020s
And you can get even more lazy and for commands that you dont't feel like typing time every time you run it, just create alias.
alias wget="time wget"
Because in bash aliases are run before other commands you can do it this way even if it looks like recursion. Then you will call it as you are used to.
And of course, aliases and prompt settings can be put in your .bashrc file, so you don't have to type them every time you open terminal.
I'm working on bash Unix shell. My script executes five 4gl files in order.if an error occurs in any of the files, the Scrip stops. But the problem is when we clear the error in the file and execute the script again..it should start from when it stopped (i.e from the step where the error is cleared)not from the first file.
You'll have to do this yourself:
if ! some-check-for-first-commmand; then
do-something-with first.4gl
fi
if ! some-check-for-second-commmand; then
do-something-with second.4gl
fi
# and so on
where "some-check-for-..." is something you write that checks if that 4gl file has been processed. It might look for some text in an output file, or the timestamp of an output file, or whatever you can do.
I've just seen the following in a script and I'm not sure what it means:
.............
started=$STATUSDIR/.$EVENT_ID-started
errs=$STATUSDIR/.$EVENT_ID-errors
# started is used to capture the time we started, so
# that it can be used as the latest-result marker for
# the next run...
>"$started"
>"$errs"
# store STDERR on FD 3, then point STDERR to $errs
exec 3>&2 2>"$errs"
..............
Specifically, the ">" at the beginning of the lines. The script actually fails with "No such file or directory". The vars are all supplied via subsidiary scripts and there doesn't seem to be any logic to create the directories it's complaining about.
It's not the easiest thing to Google for, so I thought I'd ask it here so that future bash hackers might find the answers you lovely people are able to provide.
This is a redirection. It's the same syntax used for echo hello >file (or its less-conventional but equally-correct equivalent >file echo hello), just without the echo hello. :)
When attached to an empty command, the effect of a redirection is identical to what it would be with a command that ran and immediately exited with no output: It creates (if nonexistent) or truncates (if existent) the output file, closes that file, and lets the script move on to the next command.
I am learning Bash script.
Inside my script, I've tried clear and sleep 1.
sleep 1 runs as expected but clear runs with command not found error.
Why is that?
clear and sleep 1 are enclosed within backslashes in my script. Stack Overflow is using those symbols to indicate code, and I don't know how to use escape symbols to type them here.
I assume you are trying this :
$(clear)
Or this
`clear`
These are two ways of expression command substitution. The first one is more readable and should be preferred, but they do the same thing.
The solution is to simply use this:
clear
Now, if you are interested in understanding why you are getting this error, here is a longer explanation.
Command substitution captures the output of the command enclosed -- the output of standard output (file descriptor 1), not standard error (standard error), to be more precise -- and then provides this output as a string to be used as part of the command where it was found, as if it had been part of the command to start with (but not subject to further expansions).
The clear command has a special output that causes the terminal screen to clear. But by enclosing clear in backticks, this special output is not sent to the terminal (the terminal does not clear), and it is instead captured by the command substitution. Then, this special output is provided as if it had been typed on the command line, and since it is the first (and only) thing on that line, the shell tries to find a command with a name equal to that special character, which it does not find, and that is where you get the "command not found" error.
Just for fun, try this :
$(clear >&2)
It will clear the screen, and not trigger an error, because the output is redirected to file descriptor 2 (standard error), which is not captured by the command substitution and actually is sent to the terminal (which clears), and since there is no other output, the command substitution evaluates to an empty string, which Bash interprets as a request to do nothing (Bash does not try to find a command with a zero-length name).
I hope this helps you understand the reason you are getting this error.
The backquotes are used in Stack Overflow indicating the a string should be shown as code. Your real code doesn't need backquotes:
echo "Hello from commandline"
sleep 3
echo "After sleeping, will try to use clear in 4 seconds"
sleep 1
echo "will try to use clear in 3 seconds"
sleep 1
echo "will try to use clear in 2 seconds"
sleep 1
echo "will try to use clear in 1 seconds"
sleep 1
clear
When you have seen backquotes in Unix code, you see an old fashioned way to call another command during the execution of a command.
echo "Current time is `date`, so hurry!"
Now we write this as
echo "Current time is $(date), so hurry!"
It is one char more in this simple case, but much better when nesting more things. I will not even try to write the next example with backquotes
echo "Previous command returned $(echo "Current time is $(date), so hurry!")."
I am looking for a workaround for processes with a long duration.
There is the special parameter $_ containing the last parameter of the last command.
Well I am asking you for something vice versa.
For example:
/etc/init.d/service stop; /etc/init.d/service start
.. could be easier if there is a parameter/variable containing the last binary/script called. Let's define it as $. and we get this:
/etc/init.d/service stop; $. start
Do you have any Idea how to get this?
I found this Thread on SO
But I only get output like this:
printf "\033]0;%s#%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
But the var $BASH_COMMAND is working well:
# echo $BASH_COMMAND
echo $BASH_COMMAND
# echo $BASH_VERSION
4.1.2(1)-release
Any help is very appreciated!
Thank you,
Florian
You can re-execute the last command by using:
!!
however, this won't help with what you want to do, so you could try using the "search and replace on last command" shortcut:
^<text to search for>^<text to replace with>^
so your problem could be solved using:
/etc/init.d/service stop; ^stop^start^
NOTE: This will only replace the first instance of the search text.
Also, see the comments below by more experienced peeps, for other examples and useful sources.
If the primary problem is the duration of the first process, and you know what the next process will be, you can simply issue a wait command against the first process and follow it with the second.
Example with backgrounded process:
./longprocess &
wait ${!}; ./nextprocess # ${!} simply pulls the PID of the last bg process
Example with manual PID entry:
./longprocess
# determine PID of longprocess
wait [PID]; ./nextprocess
Or, if it is always start|stop of init scripts, could make a custom script like below.
#/bin/bash
#wrapperscript.sh
BASESCRIPT=${1}
./$BASESCRIPT stop
./$BASESCRIPT start
Since the commands are wrapped in a shellscript, the default behavior will be for the shell to wait for each command to complete before moving on to the next. So, execution would look like:
./wrapperscript.sh /etc/init.d/service