Bash prompt variables does not work inside a bash function - linux

I'm working on a prompt customization, but for some reason, when I use the \u, \h and \W variables as is it works perfectly, but when I put them inside a function, they are displayed as "\u" or "\W" instead of their values.
...
print_user()
{
echo -e "\001\u\002#\001\h\002"
}
print_dir()
{
echo -e "\001${YELLOW}\002\001\W\002\001${RESET_ATTR}\002"
}
PS1='[$(print_user) on $(print_dir)] $(get_git_repo) \001\n\002$(print_prompt) '
This displays as:
[\u#\h on \W]
>
If I move them outside of the function like so
PS1='[\[\u\]#\[\h\] \[${YELLOW}\]\[\w\]\[${RESET_ATTR}\]] $(get_git_repo) \[\n\]$(print_prompt)'
It works fine, and displays the current directory with the username and hostname:
[myusername#arch on ~]
>
Is this just how bash works? Is there a different way of doing it so it will work? Why is it that inside of a function it won't display the variables' values but outside of a function it does?

From the man page, under PROMPTING
Bash allows these prompt strings to be customized by inserting a number of
backslash-escaped special characters that are decoded as follows:
[...]
After the string is decoded, it is expanded via parameter expansion, command substitution, arithmetic expansion, and quote removal, subject to the value of the
promptvars shell option (see the description of the shopt command under SHELL BUILTIN COMMANDS below).
By the time the shell expands $(print_user) to add \u to the string, it is too late to decode it, so the literal string \u remains in the prompt.
One alternative is to use PROMPT_COMMAND to execute a function that defines PS1 dynamically, just before it is displayed, instead of embedding command substitution in the value of PS1 itself.
make_prompt () {
PS1="[$(print_user) on $(print_dir)] $(get_git_repo)"
PS1+='\[\n\]'
PS1+="$(print_prompt) "
}
PROMPT_COMMAND=make_prompt
Now, print_user will have been called before the shell decodes the value of PS1, by which time all the prompt escapes will be present.

Related

What does the linux operator $ do?

I've seen many examples where $USER and similar commands are used but I could never figure out what it meant.
Whenever I search $ on Google, it doesn't recognise the symbol.
it is used to access system variable.
for example:
if you type in linux and/or unix shell
...$ my_var="some value"
...$ echo my_var
will print "some value"
...$ echo $USER
will print the name of shell user
...$ echo $?
will print the result of the previous command when successfully will print 0
in other word the result of "exit (num)" of you shell.
"$" also can indicate you are logged as no-root user for some shells the root will be indicated "#"
The $ is a special character that tells the shell interpreter to interpret the contents following as a value for a variable. It is also called variable substitution.
In the case of commands or command output, it can be used to call a shell command and store it's output as a variable's value. For example:
VAR_1="$(ip link show)"
Calling the variable VAR_1 will print the output of the command ip link show.
This is called command substitution.
You can find out more information on special characters Here
as well as information on wildcards, keywords and more.

How do I pass ">>" or "<<" to my script without the terminal trying to interpret it as me either appending to something or getting stdin?

My python script can take a series of bitwise operators as one of its arguments. They all work fine except for "=<<" which is roll left, and "=>>" which is roll right. I run my script like ./script.py -b +4,-4,=>>10,=<<1, where anything after -b can be any combination of similar operations. As soon as the terminal sees "<<" though, it just drops the cursor to a new line after the command and asks for more input instead of running the script. When it sees ">>", my script doesn't process the arguments correctly. I know it's because bash uses these characters for a specific purpose, but I'd like to get around it while still using "=>>" and "=<<" in my arguments for my script. Is there any way to do it without enclosing the argument in quotation marks?
Thank you for your help.
You should enclose the parameters that contain special symbols into single quotation marks (here, echo represents your script):
> echo '+4,-4,=>>10,=<<1'
+4,-4,=>>10,=<<1
Alternatively, save the parameters to a file (say, params.txt) and read them from the file onto the command line using the backticks:
> echo `cat params.txt`
+4,-4,=>>10,=<<1
Lastly, you can escape some offending symbols:
> echo +4,-4,=\>\>10,=\<\<1
+4,-4,=>>10,=<<1

How to get the complete calling command of a BASH script from inside the script (not just the arguments)

I have a BASH script that has a long set of arguments and two ways of calling it:
my_script --option1 value --option2 value ... etc
or
my_script val1 val2 val3 ..... valn
This script in turn compiles and runs a large FORTRAN code suite that eventually produces a netcdf file as output. I already have all the metadata in the netcdf output global attributes, but it would be really nice to also include the full run command one used to create that experiment. Thus another user who receives the netcdf file could simply reenter the run command to rerun the experiment, without having to piece together all the options.
So that is a long way of saying, in my BASH script, how do I get the last command entered from the parent shell and put it in a variable? i.e. the script is asking "how was I called?"
I could try to piece it together from the option list, but the very long option list and two interface methods would make this long and arduous, and I am sure there is a simple way.
I found this helpful page:
BASH: echoing the last command run
but this only seems to work to get the last command executed within the script itself. The asker also refers to use of history, but the answers seem to imply that the history will only contain the command after the programme has completed.
Many thanks if any of you have any idea.
You can try the following:
myInvocation="$(printf %q "$BASH_SOURCE")$((($#)) && printf ' %q' "$#")"
$BASH_SOURCE refers to the running script (as invoked), and $# is the array of arguments; (($#)) && ensures that the following printf command is only executed if at least 1 argument was passed; printf %q is explained below.
While this won't always be a verbatim copy of your command line, it'll be equivalent - the string you get is reusable as a shell command.
chepner points out in a comment that this approach will only capture what the original arguments were ultimately expanded to:
For instance, if the original command was my_script $USER "$(date +%s)", $myInvocation will not reflect these arguments as-is, but will rather contain what the shell expanded them to; e.g., my_script jdoe 1460644812
chepner also points that out that getting the actual raw command line as received by the parent process will be (next to) impossible. Do tell me if you know of a way.
However, if you're prepared to ask users to do extra work when invoking your script or you can get them to invoke your script through an alias you define - which is obviously tricky - there is a solution; see bottom.
Note that use of printf %q is crucial to preserving the boundaries between arguments - if your original arguments had embedded spaces, something like $0 $* would result in a different command.
printf %q also protects against other shell metacharacters (e.g., |) embedded in arguments.
printf %q quotes the given argument for reuse as a single argument in a shell command, applying the necessary quoting; e.g.:
$ printf %q 'a |b'
a\ \|b
a\ \|b is equivalent to single-quoted string 'a |b' from the shell's perspective, but this example shows how the resulting representation is not necessarily the same as the input representation.
Incidentally, ksh and zsh also support printf %q, and ksh actually outputs 'a |b' in this case.
If you're prepared to modify how your script is invoked, you can pass $BASH_COMMANDas an extra argument: $BASH_COMMAND contains the raw[1]
command line of the currently executing command.
For simplicity of processing inside the script, pass it as the first argument (note that the double quotes are required to preserve the value as a single argument):
my_script "$BASH_COMMAND" --option1 value --option2
Inside your script:
# The *first* argument is what "$BASH_COMMAND" expanded to,
# i.e., the entire (alias-expanded) command line.
myInvocation=$1 # Save the command line in a variable...
shift # ... and remove it from "$#".
# Now process "$#", as you normally would.
Unfortunately, there are only two options when it comes to ensuring that your script is invoked this way, and they're both suboptimal:
The end user has to invoke the script this way - which is obviously tricky and fragile (you could however, check in your script whether the first argument contains the script name and error out, if not).
Alternatively, provide an alias that wraps the passing of $BASH_COMMAND as follows:
alias my_script='/path/to/my_script "$BASH_COMMAND"'
The tricky part is that this alias must be defined in all end users' shell initialization files to ensure that it's available.
Also, inside your script, you'd have to do extra work to re-transform the alias-expanded version of the command line into its aliased form:
# The *first* argument is what "$BASH_COMMAND" expanded to,
# i.e., the entire (alias-expanded) command line.
# Here we also re-transform the alias-expanded command line to
# its original aliased form, by replacing everything up to and including
# "$BASH_COMMMAND" with the alias name.
myInvocation=$(sed 's/^.* "\$BASH_COMMAND"/my_script/' <<<"$1")
shift # Remove the first argument from "$#".
# Now process "$#", as you normally would.
Sadly, wrapping the invocation via a script or function is not an option, because the $BASH_COMMAND truly only ever reports the current command's command line, which in the case of a script or function wrapper would be the line inside that wrapper.
[1] The only thing that gets expanded are aliases, so if you invoked your script via an alias, you'll still see the underlying script in $BASH_COMMAND, but that's generally desirable, given that aliases are user-specific.
All other arguments and even input/output redirections, including process substitutiions <(...) are reflected as-is.
"$0" contains the script's name, "$#" contains the parameters.
Do you mean something like echo $0 $*?

Backslashes are removed unless quoted in command line flags

I'm using the flag package to interpret flags entered at the command line.
I created a variable using
ptrString := flag.String("string", "", "A test string")
flat.Parse()
Then when I want to print it,
fmt.Println("You entered " + *ptrString)
If I enter something like -string=hello! as a command line argument, it prints "hello!"
If I enter something like -string=hello\Bob as a command line argument, it prints "helloBob"
Is there a recommended way to convert or interpret the flag argument to a string that doesn't remove the backslash? (This is being tested on Linux and OS X, if the shell is interfering...)
Characters that have special meaning in the shell need to be quoted or escaped. You can find complete list in the shell's man pages (under "Quoting" in man 1 bash).
In this case, you can either quote or escape the baskslash
-string=hello\\Bob
// or
-string='hello\Bob'

Parsing a variable in shell scripting

I am new to shell scripting just started off.
I have written this script
#!/bin/sh
profile_type= cat /www/data/profile.conf
echo $profile_type
the o/p of this script is
. /tmp/S_panicA1.txt
. /tmp/S_panicA0.txt
away_Def="panicA1 panicA0"
away_Byp=0
away_Sts=$((panicA1+panicA0-away_Byp))
In this i want to get panicA1 panicA0 and 0 and store it in other variable how to do this?
When you want to assign the output of a command to a variable, you use the dollar parenthesis syntax.
foo=$(cat /my/file)
You can also use the backticks syntax.
foo=`cat /my/file`
In your script, you simply run the command cat and assign its result, nothing, to your variable. Hence the output consisting of the content of your file, result of cat, followed by an empty line, result of echo with an empty variable.

Resources