Ok, this is going to seem like a basic question at first, but please hear me out. It's more complex than the title makes it seem!
Here is the goal of what I am trying to do. I would like to output to console similar to Linux boot.
Operating system is doing something... [ OK ]
Now this would seem to be obvious... Just use printf and set columns. Here is the first problem. The console needs to first print the action
Operating system is doing something...
Then it needs to actually do the work and then continue by outputting to the same line with the [ OK ].
This would again seem easy to do using printf. Simply do the work (in this case, call a function) and return a conditional check and then finish running the printf to output either [ OK ] or [ FAIL ]. This technically works, but I ran into LOTS of complications doing this. This is because the function must be called inside a subshell and I cant pass certain variables that I need. So printf is out.
How about just using echo -n? That should work right? Echo the first part, run the function, then continue echoing based on the return to the same line. The problem with this solution is I can no longer preserve the column formatting that I can with printf.
Operating system is doing something... [ OK ]
Operating system is doing something else... [ OK ]
Short example... [ OK ]
Any suggestions how I can fix any of these problems to get a working solution? Thanks
Here is another way I tried with printf. This gives the illusion of working, but the method is actually flawed because it does not give you the progress indication, ie the function runs first before it ever prints out the the function is running. The "hey im doing stuff" prints immediately with the "hey im done" message. As a result, its pointless.
VALIDATE $HOST; printf "%-50s %10s\n" " Validating and sanitizing input..." "$(if [ -z "$ERROR" ]; then echo "[$GREEN OK $RESET]"; else echo "[$RED FAIL $RESET] - $ERROR"; echo; exit; fi)"
There's no particular reason all the printf strings have to be printed together, unless you're worried some code you call is going to move the cursor.
Reordering your example:
printf "%-50s " " Validating and sanitizing input..."
VALIDATE $HOST
if [ -z "$ERROR" ]; then
printf "%10s\n" "[$GREEN OK $RESET]";
else
printf "%10s\n" "[$RED FAIL $RESET] - $ERROR"
echo
exit
fi
I have no idea what $ERROR contains or where it is supposed to display.
Related
I am using git bash on windows 10 with miniconda for my python environments, and I've been trying to modify my prompt to show the environment name. So I have my ~/.bashrc file, where I tried to write a very simple condition inspired from the git-prompt.sh. The result I want is the following:
user#laptop: working_dir (git_branch)
>
and
user#laptop: working_dir (git_branch)
(conda_env) >
when I activate an environment. My problem is that I can't find a way to show the (conda_env) properly. I've tried something like this for the 2nd line:
PS1="$PS1"'\n' # new line
if [ ! -z "$CONDA_DEFAULT_ENV" ]
then
PS1="$PS1""($CONDA_DEFAULT_ENV) "
fi
PS1="$PS1"'\[\033[32m\]' # change to green
PS1="$PS1"'> '
I also tried different test for the condition, such as:
if [[ "$CONDA_DEFAULT_ENV" != "" ]]
if [ test -n "$CONDA_DEFAULT_ENV"]
and a few others. But I always have problems, sometimes it's the parentheses who show up even when $CONDA_DEFAULT_ENV is null, sometimes the test seems to work but I still have a stray space before the final ">" (which would logically come from the "($CONDA_DEFAULT_ENV) " part, meaning the test is not correct), etc.
Does anyone knows why this happens and how I can have this simple conditional prompt working?
I had the same problem just 2 hours ago, finally, I just found a solution!
The solution is to encapsulate your if statement inside a function and calling it via the string this way it's dynamic and changes when you change environments.
Let me show you how I did it:
check_conda_env ()
{
if [ ! -z "$CONDA_DEFAULT_ENV" ]; then
printf "($CONDA_DEFAULT_ENV) "
else
printf ""
fi
}
PS1="$PS1"'\n' # new line
PS1="$PS1"'$(check_conda_env)' # calls check_conda_env everytime it is printed to the screen
PS1="$PS1"'\[\033[32m\]' # change to green
PS1="$PS1"'> '
The reason why this works is simply because your string is now "dynamic", meaning it calls the function check_conda_env every time it is printed to the screen.
I have a weird situation where Bash doesn't seem to be assigning one of my variables correctly, or it might be to do with pipes. I'm honestly not sure. Here's the code:
test=0
if [ "$#" -ne 4 ]; then
echo "Error: You have passed an incorrect number of parameters" > client.pipe
test=1
if [ $test -eq 1 ]; then <-------THIS SOMETIMES DOESN'T EXECUTE
echo "end_result" > client.pipe
exit 1
What's going on here is this is a server script for a command: 'select'.
The select command takes in 4 parameters: an id (to tell it which pipe to output messages to), a database name, a table name, and then a string of column ids.
At the other end of the client pipe, the client script is listening for messages, and if it receives 'end_result' it stops listening. As you can see, no matter what happens 'end_result' should get sent back to the pipe and printed by the client script, but sometimes it doesn't happen, and I get stuck in an infinite while loop. Here's the client script so you can see where the 'listening' is happening.
while true; do
read message < client.pipe
if [ "$message" == "end_result" ]; then
echo $message
break
else
echo $message
fi
done
If I pass in the incorrect number of parameters to the first script, it prints out 'Error: you have passed an incorrect num of parameters' to the pipe, but then sometimes it doesn't assign 1 to test, and it doesn't then send 'end_result' to the pipe and exit. I can't really see what the issue is, and as I've said it works probably 7 times out of 10... I can get around the issue by having the parent script send 'end_result' to the client, but it's a bit of a hack.
I'd really appreciate it if anyone could see what the issue is, and I'm happy to provide more info about the code if required.
Many thanks, R
EDIT:
I'm almost certain the problem is to do with reading from the client pipe and that while loop, as though something were getting stuck in the pipe...
This was the solution, provided by #WilliamPursell:
while true; do
read message
if [ "$message" == "end_result" ]; then
echo $message
break
else
echo $message
fi
done < client.pipe
I've poured over question after question searching here and on Google, but something in my syntax is messed up. Any assistance is appreciated.
Basically I've got another function that sets the load and threshold, as well as $FORCE variable. The script needs to do the "stuff" if it meets either the first or the second condition. The variables have been set correctly, which I've confirmed via echo in the script while debugging.
if ([ $LOAD -ge "$THRESH" ] || [ $FORCE=1 ]);
then
# do some other stuff
fi
From what I can see, my spacing of the brackets around the conditions are correct. They no longer produce bash [: missing]'` errors like they were.
The script runs fine, except for one issue...it runs no matter what. It's like it's completely ignoring the [ $FORCE=1 ] part, even though I can see that $FORCE is in fact actually 0.
Should I be using single quotes or some other method?
Try putting spaces around =:
if ([ $LOAD -ge "$THRESH" ] || [ $FORCE = 1 ]);
then
# do some other stuff
fi
How do I fix this error? I can't see anything wrong with my syntax.
ipcheck() {
echolog "[INFO] Enabling IP Forwarding..."
sudo echo 1 > /proc/sys/net/ipv4/ip_forward
if[$(cat /proc/sys/net/ipv4/ip_forward) == "0"]
then
echolog "[CRITICAL] Could not enable IP Forwarding!"
exit 0
fi
echolog "[INFO] IP Forwarding successfully enabled!"
}
I know this is a very basic script, but it's part of a bigger one. The error happens on the then statement.
Shell scripting tends to be a lot more whitespace sensitive than you might be used to if you've come from other programming languages (read: C). Your if line has the problems. You are probably looking for:
if [ $(cat /proc/sys/net/ipv4/ip_forward) == "0" ]
The thing to remember here is that [ is not part of any special if syntax - it's the name of a program (sometimes a shell builtin). If you think of it like that, you can see how the command line parser needs it to be separated. Similarly, the [ command (or builtin) expects the closing ] to be separated from its other arguments, so you need a space before it, too.
The problem is that you need a space between if and [. The lack of a space is confusing bash's parser.
Place a space between the if and [$(cat...] section on line 4. For this script to run, you'll also need a space on the ] on the same line.
On a related note, if you're not using indentation in your shell scripts, you should seriously consider it as it makes maintenance and legibility of your code much easier.
Slightly refactored (improved) version that is not bash dependant:
#!/bin/sh
ipcheck() {
echolog "[INFO] Enabling IP Forwarding..."
sudo echo 1 > /proc/sys/net/ipv4/ip_forward || {
echolog "[CRITICAL] Error echoing to ip_forward file"
exit 1
}
# Paranoia check :)
status=$(cat /proc/sys/net/ipv4/ip_forward)
[ "$status" = "1" ] || {
echolog "[CRITICAL] Could not enable IP Forwarding!"
exit 1 # 1 is error in scripts, 0 is success
}
echolog "[INFO] IP Forwarding successfully enabled!"
}
Preferably make an error() function, perhaps something like:
# Call it like: error "[INFO] Could not ..."
error() {
echolog "$*"
exit 1
}
Oh and yeah, as everyone else pointed out, don't forget the spaces :)
During the process of writing a script, I will use the command's output in varying ways, and to different degrees - in order to troubleshoot the task at hand.. For example, in this snippet, which reads an Application's icon resource and returns whether or not it has the typical .icns extension...
icns=`defaults read /$application/Contents/Info CFBundleIconFile`
if ! [[ $icns =~ ^(.*)(.icns)$ ]]; then
echo -e $icns "is NOT OK YOU IDIOT! **** You need to add .icns to "$icns"."
else
echo -e $icns "\t Homey, it's cool. That shits got its .icns, proper."
fi
Inevitably, as each bug is squashed, and the stdout starts relating more to the actual function vs. the debugging process, this feedback is usually either commented out, silenced, or deleted - for obvious reasons.
However, if one wanted to provide a simple option - either hardcoded, or passed as a parameter, to optionally show some, all, or none of "this kind" of message at runtime - what is the best way to provide that simple functionality? I am looking to basically duplicate the functionality of set -x but instead of a line-by rundown, it would only print the notifications that I had architected specificically.
It seems excessive to replace each and every echo with an if that checks for a debug=1|0, yet I've been unable to find a concise explanation of how to implement a getopts/getopt scheme (never can remember which one is the built-in), etc. in my own scripts. This little expression seemed promising, but there is very little documentation re: 2>$1 out there (although I'm sure this is key to this puzzle)
[ $DBG ] && DEBUG="" || DEBUG='</dev/null'
check_errs() {
# Parameter 1 is the return code Para. 2 is text to display on failure.
if [ "${1}" -ne "0" ]; then
echo "ERROR # ${1} : ${2}"
else
echo "SUCESSS "
fi }
Any concise and reusable tricks to this trade would be welcomed, and if I'm totally missing the boat, or if it was a snake, and it would be biting me - I apologize.
One easy trick is to simply replace your "logging" echo comamnd by a variable, i.e.
TRACE=:
if test "$1" = "-v"; then
TRACE=echo
shift
fi
$TRACE "You passed the -v option"
You can have any number of these for different types of messages if you wish so.
you may check a common open source trace library with support for bash.
http://sourceforge.net/projects/utalm/
https://github.com/ArnoCan/utalm
WKR
Arno-Can Uestuensoez