I'm trying to set up a little shell script using the linux command "script" to log every input on my Kali Linux machine.
#!/bin/bash
now=$(date +"%m_%d_%Y_%H:%M:%S")
script /root/Logs/log_$now.txt
The script itself seems to work but i want to add it to the bash autostart, so whenever i open a terminal, my shellscript gets executed.
I tried adding it to my .bashrc file but when I open a terminal now, the script gets looped.
I added a simple "echo 'test'" script and it only starts once on terminal launch.
Adding the script to my .profile file and executing .profile manually works as intended, but as soon as i enter a script using the "script" command to my .bashrc, it gets looped.
Thank you in advance.
A new terminal window is one way of starting a new interactive shell, but so is running script. You only want to run script in the first case, not in every case.
script itself sets a variable in the environment to let you know if you are already in a shell started by script. Check for that variable before trying to run script again.
if [[ -z $SCRIPT ]]; then
now=$(date +"%m_%d_%Y_%H:%M:%S")
script /root/Logs/log_$now.txt
fi
The value of SCRIPT, if set, is the name of the file being logged to.
Alternatively, you can configure your terminal emulator to run script directly, rather than having it continue to open an ordinary interactive shell and you trying to alter its configuration.
The above applies to BSD script; for GNU script, you'll have to set such a variable yourself.
if [[ -z $SCRIPT ]]; then
now=$(date +"%m_%d_%Y_%H:%M:%S")
export SCRIPT=/root/Logs/log_$now.txt
script "$SCRIPT"
fi
The script(1) command opens a new interactive shell.
The file .bashrc runs on every interactive bash shell that is started, hence your infinite recursion.
If you want something to run only on the login shell, you put it into .bash_profile.
This should avoid the infinite recursion.
Related
I am writing a training tool, it is written in bash to teach bash/unix.
I want a script to run to set things up, then to hand control to the user.
I want it to be easily runnable by typing ./script-name
How do I do this?
I.E.
User types: tutorial/run
The run-tutorial script sets things up.
The user is presented with a task. (this bit works)
The command prompt is returned, with the shell still configured.
Currently it will work if I type . tutorial/bashrc
There are several options:
You start script in the same shell, using source or .;
You start a new shell but with your script as a initialization script:
The first is obvious; I write a little bit more details about the second.
For that, you use --init-file option:
bash --init-file my-init-script
You can even use this option in the shebang line:
#!/bin/bash --init-file
And then you start you script as always:
./script-name
Example:
$ cat ./script-name
#!/bin/bash --init-file
echo Setting session up
PS1='.\$ '
A=10
$ ./script-name
Setting session up
.$ echo $A
10
.$ exit
$ echo $A
$
As you can see, the script has made the environment for the user and then has given him the prompt.
Try making it an alias in your ~/.bashrc file. Add this to the bottom of ~/.bashrc:
alias tutorial='. tutorial/bashrc'
Then close and re-open your terminal, or type . ~/.bashrc to re-source it.
To use this alias, simply call tutorial, and that will automatically get replaced with its alias, as though you had called . tutorial/bashrc.
when I am in a Cygwin terminal, I can easily use the "source" command.
For example, let's say I have a file called "my_aliases.sh", that contains the following
#!/bin/bash -f
alias clear='cmd /c cls'
#unalias clear
Then on the Cygwin terminal, I can type
$source my_aliases.sh
And it just works fine, and whenever I type "clear", I can see that it works.
But I don't know why doing the same thing inside another shell script, and calling that shell script doesn't work.
For example, let's say that I have a file called "run_alias.sh", with the following content:
#!/bin/bash -f
#
a=`source my_aliases.sh`
b=`ls -ltr`
echo $a
echo $b
And when I try to run my file
$ ./run_alias.sh
It just doesn't do anything. For example, I can see that the command (b) takes place, but nothing happens for command (a).
But after I run "run_alias.sh", and type "clear", I get the following error:
$ clear
bash: clear: command not found
I even tried to change run_alias.sh as follows:
#!/bin/bash -f
echo `source my_aliases.sh`
But now when run run_alias.sh, and type clear, I get the exact same error message !!!
Any idea how to call the "source" command from some other shell script in Cygwin?
A child process cannot alter its parent's environment.
When you execute the run_alias.sh script, you launch a new bash process, which sources your alias file. Then the script ends, that bash process terminates and it takes its modified environment with it.
If you want your aliases to be automatically available, source it from your $HOME/.bashrc file.
Backticks create a subshell. The changes made to your environment in that subshell do not affect the calling environment.
Id you want your script (run_alias.sh) to have access to the environment in my_aliases.sh, call source directly.
source my_aliases.sh
b=`ls -lrt`
echo $b
and if you want the changes that run_alias.sh makes to its environment to propagate to it's parent, run source on the command line.
$ source run_alias.sh
I have a hard time with interactive and non-interactive shells. I don't understand which is which.
For example, I have read that non interactive shells usually check for the BASH_ENV variable on their startup and execute whatever it points to.
So, what I did is I set the BASH_ENV to point to some script which only echoes OK. Then I typed in bash in terminal and this script echoed OK. But why? Didn't I call yet another INTERACTIVE shell by typing bash in terminal, and not the other way around? Why did it execute the bash_env? I'm on linux mint maya.
The only thing you can be certain of is what's shown in the manpage for bash (see INVOCATION) - that lists in details what startup files are run in each instance.
However, there's nothing stopping (for example) one of those startup files running other files which would normally not be run.
By way of example, if .bash_profile had the following line:
. ~/.profile
it would also run the .profile script.
In fact the manpage states:
When bash is started non-interactively, to run a shell script, for example, it looks for the variable BASH_ENV in the environment, expands its value if it appears there, and uses the expanded value as the name of a file to read and execute. Bash behaves as if the following command were executed:
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
So, if you put that exact line in your startup scripts for an interactive shell like ~/.bash_profile, you'll also source the file pointed to by BASH_ENV.
Your best bet is to examine the INVOCATION section to find out which of the files will run, and then track through them (with something like set -x at the top of the script) to see what's getting called from where.
If memory serves, Bash is only interactive if you tell it, example
bash -i
So, by you calling just bash you invoked a non-interactive Bash.
More info
-i
If the -i option is present, the shell is interactive.
I need to set some variables in my shell and format the shell prompt before I do a certain task every time. Also I need to record my actions.
At the moment, I'm running a script, collecting all the information it needs (partly by user input) and it sets the new shell prompt and the variables. Then I start the recording with script $var-actionlog-$anothervar-1.log. The variables used are set during the call of my_script.sh. Then, since a new shell spawned by the script command, I need to call source my_script.sh again.
Is there a way that I can include the script-part directly in my_script.sh so that I don't have to enter everything twice?
You can use export in your initial setup script to make the local variables global. For example, your setup script, my_script.sh could be:
#!/bin/sh
# A startup script
export var="world"
export PS1="\u#\h \w> "
echo "Please enter a number, followed by [ENTER]:"
read num
export anothervar="$num"
script $var-actionlog-$anothervar-1.log
Then, once the script has started, to confirm you can run:
echo var=$var, anothervar=$anothervar
If you want to set a different shell prompt ($PS1) in your my_script.sh, to be used by the typescript, make sure that you are not setting $PS1 in ~/.bashrc. The typescript, script $var-actionlog-$anothervar-1.log, calls ~/.bashrc every time and will override your prompt variable, $PS1. To set your prompt for log-in shells only, set this in ~/.bash_login.
I just found this very usefull shell script here on SO but unfortunately it's not working on Mac OS X 10.5.
This is the script in question(copied it for convenience):
#!/bin/bash
LIMIT=$1
P=$PWD
for ((i=1; i <= LIMIT; i++))
do
P=$P/..
done
cd $P
I tried to echo $P at the very end and it's returning the right path, but still cd $P doesn't seem to be working.
So I tried to manually enter P="some/path" and cd $P in the terminal and it worked.
I don't get why the same command isn't working in the script. Could it be a security thing?
Any suggestions?
I've had the same issue on Linux, actually, if I understood correctly what I've found after some searching, this is what happens:
The command is launched in a subshell, and in that subshell the path gets changed, you don't see the change because when the script finishes you get back to the starting (parent) shell.
I solved this by putting that useful script in my .bashrc as a function, like this:
up(){
#code goes here
}
Another option is to source the script every time you launch it but I prefer the first one.
once the shell script ends it will put you right back in the directory it was executed from. The cd will only effect the cwd of the script process
You are only changing the working directory for the copy of the shell that is running the script as an interpreter, not the original shell program that you launched the script from.
For a bash-like shell, in order to run a sequence of commands that operate on the interactive shell session, you can define them as a shell function.
e.g. type the following
up() { LIMIT=$1; P=$PWD; for ((i=1; i <= LIMIT; i++)); do P=$P/..; done; cd $P; }
and you'll define an up command that works the way you intended.
You could put this function definition into a file that is sourced when you login, such as .bashrc, to keep it conveniently defined on login.
If you want to run the script within the context of your current shell just do one of the following (assuming your shell script is called cdup)
. cdup 3
source cdup 3
The source command (and its alias .) run the provided script within the context of your current shell, i.e. they do not start a separate sub-shell to run the command so your cd will work as it is within the current shell
A small addition to the up() - function; add the test for no value:
LIMIT=$1
if [ -z "$LIMIT" ]; then
LIMIT=1
fi
and no more "cd .." - just "up"