How to give a (new mail) notification in bash/zsh automatically? - linux

Well, the title of my question is a little vague. Now let me explain that clearly.
We know there is a "MAILCHECK" in bash, every few minutes bash will check the mailbox and give you a message if you have new mails. Note that you don't need a command for this notification. Bash print a message automatically anytime if there are new mails.
Here, I have few questions:
there is not such a notification in my zsh (maybe I forget something in my .zshrc)
how to change the format of "new mail notification" in bash/zsh
how to execute a certain command after any of my command is finished in bash/zsh. e.g. when I type ls and <enter>, ls will be executed, and then the certain command will be executed.
If I can do this, the automatic notification is done!
Is that clear? Any suggestion?

1. Mail notification in zsh:
I think it's just like bash; mail notification will take place if the shell knows where to look for mail and if the MAILCHECK parameter is set to a non-negative integer.
2. Changing the mail notification message.
(from man bash):
MAILPATH
A colon-separated list of file names to be checked for mail. The message to be
printed when mail arrives in a particular file may be specified by separating
the file name from the message with a '?'. When used in the text of the
message, $_ expands to the name of the current mailfile. Example:
MAILPATH='/var/mail/bfox?"You have mail":~/shell-mail?"$_ has mail!"'
Bash supplies a default value for this variable, but the location of the user
mail files that it uses is system dependent (e.g., /var/mail/$USER).
I think zsh is roughly the same, aside from also exposing mailpath as the array version of MAILPATH.
3. Running arbitrary commands:
In bash, the value of PS1 is printed as a command prompt. Unless the promptvars options is unset (it is set by default), the string undergoes parameter expansion, command substitution, arithmetic expansion and quote removal before being used. The second of those means that you can execute arbitrary shell commands as part of the command prompt.
zsh has the same feature, controlled by the shell option promptsubst (or PROMPT_SUBST, as the manpage says). Unlike bash, the shell options is unset by default. Also, you might find that you are unable to change the value of PS1 (if your distro uses prompt themes), because the prompt theme resets PS1 before every command prompt.
It turns out that zsh has a different mechanism for running shell functions before the prompt is printed (or in other circumstances; I'm just going to focus on this one case). There is an array parameter called precmd_functions whose values are the names of functions which will be run before every prompt. (The prompt theme systems uses this mechanism to reset PS1 before it is printed.)

I don't really know how to go about questions 1 and 2, but one way to execute a certain command after each command finished in the interactive shell prompt (question 3), is to add your code in the prompt variable PS1.
Here is an example with the date command:
$ PS1="\$(date) $ "
Fri Jun 21 22:49:00 BRT 2013 $ echo how cool is this?
how cool is this?
Fri Jun 21 22:49:02 BRT 2013 $

There's also the PROMPT_COMMAND bash variable:
PROMPT_COMMAND
If set, the value is interpreted as a command to execute before the printing of each primary prompt ($PS1).
Suppose you want to know if the previous command exited with a non-zero status:
$ PS1='\$ ' PROMPT_COMMAND='r=$?;(($r != 0)) && printf "[%d] " $r'
$ whoami
jackman
$ (exit 3)
[3] $ pwd
/home/jackman
$

Related

How to identify which REPL is running in the terminal?

I am trying to have my terminal title change depending on what that specific window is doing. So far I have the following which will update based on directory and server.
function settitle() {
if [[ -z "$ORIG" ]]; then
ORIG=$PS1
fi
TITLE="\[\e]2;$*:$(dirs -0)\a\]" #dirs -0 is like pwd but with ~/ instead of /home/user/
PS1=${ORIG}${TITLE}
}
PROMPT_COMMAND="settitle local" # local is the server name in this case
Now, sometimes I'm in the PHP (php -a) or MySQL (mysql -u user -ppass) REPL, and I'd like the title to reflect that instead of just being whatever directory I launched the REPL from.
The best I can figure is getting the last command somehow, then figuring out what the first word is, and running an if check in settitle(). I've tried everything from here and here among other places, and while I can usually get part of it to work in the command line, non of it works in settitle(). For example.
local:~$ echo 'foobar'
foobar
local:~$ !:0
echo
# I add echo !:0 to settitle()
local:~$ source .bashrc
!:0
local:~$
A note: It should be "source", or at least "echo" from before. !:0 does not recognize itself as a command so it will repeat the last real command over and over. The "!:0" being echoed is a literal string, not the results of the command. Additionally, saving to a var does not work, and just putting the command without trying to echo/save the result gives !:0: command not found.
I don't want to make this an XY problem, so if I am barking up the wrong tree here at any step of the process, please let me know. The goal is to be able to change the title of my terminal window if I enter an REPL. How can I identify when a command will enter me into one?
Note that PROMPT_COMMAND and similar shell features are not relevant when you're in a different REPL; from the point of view of the shell, the entire REPL session is one single command. The shell prompt doesn't show up again until you exit that REPL, and that's the point at which PROMPT_COMMAND and friends are activated.
One thing you can do is alias the command you use to start the REPL so that it sets the title of the window first:
alias phpa='setttitle PHP; php -a'
alias mysqli=`settitle MySQL; mysql -u "$USER"'
or something like that.
The sequence goes like this:
PROMPT_COMMAND runs.
The shell prints its prompt.
You type the command to start a REPL
You are in the REPL. The prompt you see is printed by the REPL, not the shell, which is not involved at this point. The shell is just hanging out waiting for you to exit the REPL; it's not printing any prompts, so it's not ever running PROMPT_COMMAND.
You type commands in the REPL. No matter how many you run, it's part of a single session that the shell sees as a single command.
You exit the REPL.
PROMPT_COMMAND runs.
The shell prints its prompt.

bash + Linux + how to ignore the character "!"

I want to send little script to remote machine by ssh
the script is
#!/bin/bash
sleep 1
reboot
but I get event not found - because the "!"
ssh 183.34.4.9 "echo -e '#!/bin/bash\nsleep 1\reboot>'/tmp/file"
-bash: !/bin/bash\nsleep: event not found
how to ignore the "!" char so script will so send successfully by ssh?
remark I cant use "\" before the "!" because I get
more /tmp/file
#\!/bin/bash
sleep 1
Use set +H before your command to disable ! style history substitution:
set +H
ssh 183.34.4.9 "echo -e '#!/bin/bash\nsleep 1\reboot>'/tmp/file"
# enable hostory expnsion again
set -H
I think your command line is not well formated. You can send this:
ssh 183.34.4.9 'echo -e "#!/bin/bash\nsleep 1\nreboot">/tmp/file'
When I say "not well formated" I mean you put ">" inside the "echo" and you forgot to add "n" before "reboot", and you put "\reboot", wich will be interpreted as "CR" (carriage return) followed by "eboot" command (which I don't think that exists).
But what did the trick here is to invert the comas changing (') with (") and viceversa.
Bash is running interactively (which means that you are feeding commands to it from the standard input and not exec(2)ing a command from a shell script) so you don't need to include the line #!/bin/bash in that case (even more, bash should just ignore it, but not the included bang, as it is part of the active history mechanism)
But why? the first two characters in an executable file (any file capable of being exec(2)ed from secondary storage, not your case) have a special meaning (for the kernel and for the shell): they are the magic number that identifies the kind of executable file the kernel is loading. This allows the kernel to select the proper executable loading routines depending on the binary executable format (and what allows you for example to execute BSD programs in linux kernels, and viceversa)
A special value for this magic numbers is composed by the two characters # and ! (in that order) that forces the kernel to read the complete first line of that file and load the executable file specified in that line instead, allowing you to execute shell scripts for different interpreters directly from the command line. And it is done on purpose, as the # character is commonly in shell script parlance a comment character. This only happens when the shell that is interpreting the commands is not an interactive shell. When the shell loads a script with those characters, it normally reads the first line also to check if it has the #! mark and load the proper interpreter, by replicating the kernel function that does this. Despite of being a comment for the shell, it does this to allow to treat as executables files that are not stored on secondary storage (the only ones the exec(2) system call can deal with), but coming from stdin (as happens to yours).
As your shell is running interactively and you do want to execute its commands without a shell change, you don't need that line and can completely eliminate it without having to disable the bang character.
Sorry, but the solution given about executing the shell with -H option will probably not be viable, as the shell executing the commands is the login shell in the target machine, so you cannot provide specific parameters to it (parameters are selected by the login(8) program and normally don't include arbitrary parameters like -H).
The best solution is to fully eliminate the #!/bin/bash line, as you are not going to exec(2) that program in the target. In case you want to select the shell from the input line (case the user has a different shell installed as login shell), it is better to invoke the wanted shell in the command line and pass it (through stdin, or making it read the shell script as a file) the shell commands you wan to execute (but again, without the #! line).
NOTE
Its important to ensure you'll execute the whole thing, so it's best to pass all the script contents in the destination target, and once assured you have passed the whole thing to execute it as a whole. Then your #! first line will be properly processed, as the executable will be run by means of an exec(2) made from the kernel.
Example:
DIRECTORY=/bla/bla
FILE=/path/to/file
OUTPUT=/path/to/output
# this is the command we want to pass through the line
cat <<EOF | ssh user#target "cat >>/tmp/shell.sh"
cd $DIRECTORY
foo $FILE >$OUTPUT
exit 0
EOF
# we have copied the script file in a remote /tmp/shell.sh
# and we are sure it has passed correctly, so it's ready
# for local execution there.
# now, execute it.
# the remote shell won't be interactive, and you'll ensure that it is /bin/bash
ssh user#target "/bin/bash /tmp/shell.sh" >remote_shell.out
A more sophisticate system is one that allows to to sign the shell script before sending, and verify the script signature before executing it, so you are protected against possible trojan horse attacks. But this is out of scope on this explanation.
Another alternative is to use the batch(2) command remotely and pass it all the commands you want executed. you'll get a sessionless executing environment, more suitable to the task you are demanding (despite the fact that you'll get the script output by email to the target user running the script)
Interactively, beware that ! triggers history expansion inside double quotes
from here: https://riptutorial.com/bash/example/2465/quoting-literal-text
my recommended solution is to use single quotes to define the string (and either escape single quotes \' or use double quotes " within the string):
ssh 183.34.4.9 'echo -e "#!/bin/bash\nsleep 1\reboot>"/tmp/file'

"tput: No value for $TERM and no -T specified " error logged by CRON process

We have a shell script which is run by CRON. The shell script in turn runs a python script which downloads files from an FTP server and then runs a java log processor on those files. The process runs just fine, except that I keep on getting CRON emails even if there is no error. At least, I think there is no error. The cron email has two lines, out of which one of the lines is
tput: No value for $TERM and no -T specified
After researching a bit, I found that it's something to do with setting the $TERM variable. I am not sure, how to do that. Any help would be appreciated. Thanks!
The cron daemon is run by 'root' user in its own shell. By default, cron will append a system mail sent to the user running the script (that's why you see the sender as 'root' in the system mail). The 'user' is the user you were logged in as when setting the crontab. The mail will contain console and error messages. On Ubuntu, the mail file is viewable at /var/mail/<username>.
If no $TERM variable is set, cron will emit a tput: No value for $TERM and no -T specified error in the mail file. To stop these errors, set the $TERM variable using TERM=dumb (or another available terminal in your system, like xterm) in the crontab. The toe command will show you the terminfo definitions on the current system. If you lack that command, you can see the raw data in /usr/share/terminfo on most Linux systems.
Even though you have stopped the errors, you may still get appended system mail with console messages. This file will fill up like a log over time, so you may want to stop these messages. To stop cron system mail, set the MAILTO variable using MAILTO=""
So your crontab might look like:
MAILTO=""
TERM=xterm
* * * * * sh /path/to/myscript.sh
You can view the crontab (for the user you are signed in as) with 'crontab -l'.
Something in the script is calling the tput binary. tput attempts to inspect the $TERM variable to determine the current terminal so it can produce the correct control sequences. There isn't a terminal when cron is running so you get that error from tput.
You can either manually assign a TERM value to the cron job (likely dumb or something similar to that) or (and this is likely the better solution) you can find out what is calling tput and remove that call.
--> MY WORKING SOLUTION FOR THE TPUT-PROBLEM:
# when $TERM is empty (non-interactive shell), then expand tput with '-T xterm-256color'
[[ ${TERM}=="" ]] && TPUTTERM='-T xterm-256color' \
|| TPUTTERM=''
declare -r RES='tput${TPUTTERM} sgr0' REV='tput${TPUTTERM} rev'
declare -r fRD='tput${TPUTTERM} setaf 1' bRD='tput${TPUTTERM} setab 1'
declare -r fGN='tput${TPUTTERM} setaf 2' bGN='tput${TPUTTERM} setab 2'
...
echo ${fRD}" RED Message: ${REV} This message is RED REVERSE. "${RES}
echo ${fGN}" GREEN Message: ${REV} This message is GREEN REVERSE. "${RES}
...
this way it makes no sense if there's an interactive or a non-interactive shell - tput still works fine.
6a5h4

Setting a part of a PWD as a prompt and keeping a variable updated

I'm using tcsh, and I'm trying to set a part of the PWD to appear always in the prompt (so I will always know in which "parent" directory I am).
I managed to extract the needed part of the prompt in the following way, and it works fine (I call it MyTreePath):
set MyTreePath=`echo $PWD | awk '{... print whichTree}'`
I've added the code above to my .tcshrc and I've added %$MyTreePath to my set prompt line in .tcshrc.
The problem is that once the shell is opened, the MyTreePath doesn't change, even if I'm going to a totally different path.
How to keep a variable that appears in the prompt updated?
Use the magical cwdcmd alias! It is used for defining a command which executes everytime the cwd changes. In your case, you need to updated your variable.
From the manpage:
The beepcmd, cwdcmd, periodic, precmd, postcmd, and jobcmd Special
aliases can be set, respectively, to execute commands when the
shell wants to ring the bell, when the working directory changes,
every tperiod minutes, before each prompt, before each command gets
executed, after each command gets executed, and when a job is started
or is brought into the foreground.
Here's a quick example:
alias cwdcmd 'set FOO=`pwd`'
set prompt='%$FOO >>> '
field testing:
cd /
/ >>> cd dev
/dev >>>
So all that's left is to replace pwd in the alias above with your own command.
For more info and other magic aliases, see here.

advanced unix command-line trick

I want help regarding unix command line.
In command line if i type any command like date; konsole should automatically execute ls and than execute date.
example: if i type date
than interface should be like ls ; date.
I can do alias but i want not only for date for every other command also. even if i don't supply any command and press empty enter it should execute ls.
I tried confugiring in set prompt variable. but i didn't got it and the variable is not reloading automatically.
Are you using konsole as your terminal emulator and tclsh as your interactive shell? If so, the former is irrelevant and the latter is an interesting tool choice. More likely, you are running bash as your interactive shell, in which case you can simply add a trap on DEBUG. To test the behavior type:
trap ls DEBUG
After executing this in bash, in this shell only, ls will be executed before every command. If this is the behavior you want, set the trap in ~/.bashrc.
Note that this is bash specific. Other shells have different mechanisms for getting this behavior.

Resources