Bash check if path contains two folders - bash

How can I search an arbitrary path and determine if it has two folder names? The folder names can appear in any position in either order. Not a shell expert so seeking help here.
if [ -p "$PATH" ]; then
echo "path is set"
else
echo "path is not set"
fi
I found this segment but I'm not sure it's useful. $PATH is a special variable correct?

First, let me make sure I understand the question right. You have some path (like "/home/sam/foo/bar/baz") and you want to test whether it contains two specific directory names (e.g. "foo" and "bar") in either order, right? So, looking for "foo" and "bar":
/home/sam/foo/bar/baz would match
/mnt/bar/subdir/foo would also match
/mnt/bar/foo2 would not match, because "foo2" is not "foo"
If that's correct, you can do this in bash as two tests:
dir1="foo"
dir2="bar"
if [[ "/$path/" = *"/$dir1/"* && "/$path/" = *"/$dir2/"* ]]; then
echo "$path" contains both $dir1 and $dir2"
else
echo "$path" does not contain both $dir1 and $dir2"
fi
Notes:
This is using the [[ ]] conditional expression, which is different from [ ] and not available in basic shells. If you use this type of expression, you need to start the shell script with a shebang that tells the OS to run it with bash, not a generic shell (i.e. the first line should be either #!/bin/bash or #!/usr/bin/env bash), and do not run it with the sh command (that will override the shebang).
The way the comparison works is that it sees whether the path matches both the patterns *"/$dir1/"* and *"/$dir2/"* -- that is, it matches those names, with a slash at each end, maybe with something else (*) before and after. But since the path might not start and/or end with a slash, we add them ("/$path/") to make sure they're there.
Do not use PATH as a variable in your script -- it's a very special variable that tells the shell where to find executable commands. If you ever use it for anything else, your script will suddenly start getting "command not found" errors. Actually, there are a bunch of all-caps special-meaning variables; to avoid conflicts with them, use lowercase or mixed-case variables for your things.

Related

zsh Looping through multiple parameters

In my old .bashrc, I had a short section as follows:
PATH2ADD_SCRIPTBIN="/home/foo/bar/scriptbin"
PATH2ADD_PYTHONSTUFF="/home/foo/bar/pythonprojects"
PATH2ADDLIST="$PATH2ADD_SCRIPTBIN $PATH2ADD_PYTHONSTUFF"
for PATH2ADD in $PATH2ADDLIST; do
if [ -z `echo $PATH | grep "$PATH2ADD"` ]; then
export PATH=$PATH:$PATH2ADD
echo "Added '$PATH2ADD' to the PATH."
fi
done
And in Bash, this worked just as intended: it appended the paths I included in $PATH2ADDLIST if they were not already present in the path (I had to do this after realizing how huge my path was getting each time I was sourcing my .bashrc). The output (when the provided paths were not already present) was as follows:
Added '/home/foo/bar/scriptbin' to the PATH.
Added '/home/foo/bar/pythonprojects' to the PATH.
However, I recently switched over to the magical land of Zsh, and the exact same lines of text now produce this result:
Added '/home/foo/bar/scriptbin /home/foo/bar/pythonprojects' to the PATH.
Now I'm pretty sure that this is because of some difference in how Zsh does parameter expansion, or that it has something to do with how Zsh changes the for loop, but I'm not really sure how to fix this.
Might anyone have some insight?
Use an array to store those variables, i.e.
PATH2ADD_SCRIPTBIN="/home/foo/bar/scriptbin"
PATH2ADD_PYTHONSTUFF="/home/foo/bar/pythonprojects"
# Initializing 'PATH2ADDLIST' as an array with the 2 variables
# to make the looping easier
PATH2ADDLIST=("${PATH2ADD_SCRIPTBIN}" "${PATH2ADD_PYTHONSTUFF}")
# Looping through the array contents
for PATH2ADD in "${PATH2ADDLIST[#]}"
do
# Using the exit code of 'grep' directly with a '!' negate
# condition
if ! echo "$PATH" | grep -q "$PATH2ADD"
then
export PATH=$PATH:$PATH2ADD
echo "Added '$PATH2ADD' to the PATH."
fi
done
This way it makes it more compatible in both zsh and bash. A sample dry run on both the shells,
# With interpreter set to /bin/zsh
zsh script.sh
Added '/home/foo/bar/scriptbin' to the PATH.
Added '/home/foo/bar/pythonprojects' to the PATH.
and in bash
bash script.sh
Added '/home/foo/bar/scriptbin' to the PATH.
Added '/home/foo/bar/pythonprojects' to the PATH.
zsh has a few features that make it much easier to update your path. One, there is an array parameter path that mirrors PATH: a change to either is reflected in the other. Two, that variable is declared to eliminate duplicates. You can simply write
path+=("/home/foo/bar/scriptbin" "/home/foo/bar/pythonprojects")
and each new path will be appended to path if it is not already present.
If you want more control over the order in which they are added (for example, if you want to prepend), you can use the following style:
path=( "/home/foo/bar/scriptbin"
$path
"/home/foo/bar/pythonprojects"
)
(Note that the expansion of an array parameter includes all the elements, not just the first as in bash.)

While loop in .sh file with condition to compare if a string contains a substring

I have a .sh file in which I have written the following function. The command that calls this function will have the arguments- file1.war, file2.war ... fileN.war and other arguments.
I want to do a certain operation to the .war files and something else for the arguments after it. So I have written a while loop that will run till the arguments are .war files, and when an argument is encountered without .war extention, it will exit the loop and run the code below it for the rest of the arguments.
Here is the function in .sh file :
copyWarFiles()
{
downloadFileName=$1
shift 1
extn=".war"
while [ condition ]
do
log "war file $downloadFileName .."
#some operation..
downloadFileName=$1
shift 1
done
#operations for the rest of the arguments...
}
What should I give as condition that will return true if $downloadFileName ends with .war? I tried giving
$downloadFileName==*".war" (following the accepted answer in this )
and I also tried this :
`test "${downloadFileName#*$extn}" != "$downloadFileName"`
(following the accepted answer here) where extn is another variable I declared and assigned to .war.
But in both the cases, I see that it never enters the while loop. I think I have gone wrong with the syntax or something. Thank you for your help in advance.
What should I give as condition that will return true if $downloadFileName ends with ".war"? I tried giving $downloadFileName==*".war" […]
Bash, unlike typical programming languages, doesn't recognize == as a special operator; it's just yet another argument to the [ command. So you need to set it off with spaces.
Also, the [ command doesn't support having a pattern on the right-hand-side of ==; you need to use the special [[ ... ]] notation.
So:
while [[ $downloadFileName == *".war" ]]
Note, though, that the double-quotes around .war don't actually have any effect: none of the characters in .war are special characters that need to be quoted. Conversely, it's a best practice to always put variable expansions in double-quotes, in case the variables contain special characters. ([[ actually negates most of the problematic behaviors, but it's just a good habit to be in.)
So:
while [[ "$downloadFileName" == *.war ]]
Why not just:
check=`echo $downloadFile | grep '\.war'`
if [ -n "$check" ]; then
echo $downloadFile ends in .war
fi

Writing a shell conditional for extensions

I'm writing a quick shell script to build and execute my programs in one fell swoop.
I've gotten that part down, but I'd like to include a little if/else to catch bad extensions - if it's not an .adb (it's an Ada script), it won't let the rest of the program execute.
My two-part question is:
How do I grab just the extension? Or is it easier to just say *.adb?
What would the if/else statement look like? I have limited experience in Bash so I understand that's a pretty bad question.
Thanks!
There are ways to extract the extension, but you don't really need to:
if [[ $filename == *.adb ]] ; then
. . . # this code is run if $filename ends in .adb
else
. . . # this code is run otherwise
fi
(The trouble with extracting the extension is that you'd have to define what you mean by "extension". What is the extension of a file named foo? How about a file named report.2012.01.29? So general-purpose extension-extracting code is tricky, and not worth it if your goal is just to confirm that file has a specific extension.)
There are multiple ways to do it. Which is best depends in part on what the subsequent operations will be.
Given a variable $file, you might want to test what the extension is. In that case, you probably do best with:
extn=${file##*.}
This deletes everything up to the last dot in the name, slashes and all, leaving you with adb if the file name was adafile.adb.
If, on the other hand, you want to do different things depending on the extension, you might use:
case "$file" in
(*.adb) ...do things with .adb files;;
(*.pqr) ...do things with .pqr files;;
(*) ...cover the rest - maybe an error;;
esac
If you want the name without the extension, you can do things the more traditional way with:
base=$(basename $file .adb)
path=$(dirname $file)
The basename command gives you the last component of the file name with the extension .adb stripped off. The dirname command gives you the path leading to the last component of the file name, defaulting to . (the current directory) if there is no specified path.
The more recent way to do those last two operations is:
base=${file##/}
path=${file%/*}
The advantage of these is that they are built-in operations that do not invoke a separate executable, so they are quicker. The disadvantage of the built-ins is that if you have a name that ends with a slash, the built-in treats it as significant but the command does not (and the command is probably giving you the more desirable behaviour, unless you want to argue GIGO).
There are other techniques available too. The expr command is an old, rather heavy-weight mechanism that would not normally be used (but it is very standard). There may be other techniques using the (( ... )), $(( ... )) and [[ ... ]] operators to evaluate various sorts of expression.
To get just the extension from the file path and name, use parameter expansion:
${filename##*.} # deletes everything to the last dot
To compare it with the string adb, just do
if [[ ${filename##*.} != adb ]] ; then
echo Invalid extension at "$filename".
exit 1
fi
or, using 'else`:
if [[ ${filename##*.} != adb ]] ; then
echo Invalid extension at "$filename".
else
# Run the script...
fi
Extension:
fileext=`echo $filename | sed 's_.*\.__'`
Test
if [[ x"${fileext}" = "xadb" ]] ; then
#do something
fi

How do I check if a PATH is set? And if not, set it from an argument

#!/bin/bash
if[$LD_PATH == ""]
then
export LD_PATH=$1
else
export LD_PATH=$LD_PATH
fi
#excute the program that needs the path
./someProgThatNeedsThePath
i keep getting "cannot open shared object file"
First, your bash syntax is broken due to lack of spaces. First, you would need spaces around the [ and ] closures. If you use the newer alternate double bracket syntax, you don't have to worry about quoting variables you are testing.
#!/bin/bash
if [[ $LD_PATH = "" ]]; then
export LD_PATH="$1"
else
export LD_PATH="$LD_PATH"
fi
But your real problem is not that at all. Since your code is running in a separate bash shell, the code will have no effect on anything you run in the parent shell. In order to do that you would want to build a function:
function add_to_ld_path () {
if [[ -z $LD_PATH ]]; then
export LD_PATH="$1"
fi
}
Add that code to your .profile, then run it with add_to_ld_path /my/path when you want to use it. Note that since your "else" statement wasn't actually doing anything, I removed it. I also replaced your test for a blank string by making your own with quotes with the builtin empty string test.
Next up is what you are actually trying to accomplish. If all you want to do is set the variable if it's empty, UncleAli's solution is very simple. But it might be useful to do something like this:
function add_to_ld_path () {
case ":$LD_PATH:" in
*"$1"*) :;;
*) LD_PATH=$LD_PATH:$1
esac
}
This would check the current path and add the path given if it wasn't already part of the LD_PATH variable, otherwise it would leave it alone.
if [ -z "$LD_PATH" ]
then
export LD_PATH=$1
fi
Will that do the job?

Substitute output from variable to give another output

I am trying to substitute output from variable to give another output. The variable i have problems with is the $apps. It gives me "syntax error: bad substitution".
$appletDir is a directory with desktop shortcuts. The problem is that some shortcuts do not have the same name as the icon(png). So i need to substitute the program name with the png linking to it. I got it working with the commented out if-statement below. If this substitution could work then my script would look better. Cause i need to put down a couple of this.
I want it to look for "general_call" instead of "rtcom-call-ui" when going through the icon folders. Cause the png is called "general_call". The icons folders are the variables $icoDir64 $icoDirSca.
for applet in $appletDir*
do
app=`basename $applet | sed -e 's/.*://g' -e 's/.*osso-//g' -e 's/\.desktop.*//g'`
apps="${app/rtcom-call-ui/general_call}"
#if [ "${app}" = "rtcom-call-ui" ]; then
# app="general_call"
#fi
#echo $apps
#done
#exit 0
found=`find ${icoDir64} ${icoDirSca} -name "*.png"`
for file in $found
do
base="`basename ${file}`"
if [ "${base}" = "${app}.png" -o "${base}" = "tasklaunch_${app}.png" -o "${base}" = "general_${app}.png" ]; then
echo "WORKING!!!!!!!!!!!!!!!!!! $file"
fi
done
done
I think you may have a shell version problem (your shell isn't as modern as the notation you are using). A previous incarnation of this post suggested:
apps="${app}/rtcom-call-ui/general_call"
Or, for substituting rtcom-call-ui with general_call, you need to use echo and sed (at least in classic shells - it might be that bash has something built-in to do it):
apps=$(echo "${app}" | sed s/rtcom-call-ui/general_call/)
The notation ${var|continuation} (where | represents an arbitrary punctuation character) is used to modify the value substituted. For example:
apps="${app:-/something/suitable/as/the/default}"
would copy the value of $app, unless $app is not set at all (not relevant here; useful with environment variables) or if $app is an empty string.
The error you are getting is because there is no valid substitution that starts with '/' in your version of the shell. This notation seems to be valid in some versions of Bash (including the one I have to play with); I don't know when it was added. But if the shell you are using is complaining about the notation, then clearly it is not correct for the version of the shell you are using.
Depending on the shebang line (#!/bin/sh vs #!/bin/bash), it might work differently. Failing that, the version of Bash on your machine may be too old.
You can check your shell(s) with:
for app in /some/location/rtcom-call-ui/where.png /another/location/nowhere/thing.png
do
apps=${app/rtcom-call-ui/general-call}
echo $app
echo $apps
done

Resources