What's wrong with this bash script using cut and sed (cut: command not found)? - bash

I'm getting server and path from an NFS location in bash as follows:
#!/bin/bash
ST="/net/10.111.111.111/path/to/some/dir"
echo $ST
SERVER=$(echo $ST | cut -d'/' -f3)
echo $SERVER
PATH=$(echo $ST | cut -d'/' -f4-)
echo $PATH
PATH=$(echo $ST | cut -d'/' -f4-)
echo $PATH
The same 2 lines are repeated above on purpose. The output is:
/net/10.111.111.111/path/to/some/dir
10.111.111.111
path/to/some/dir
./nn.sh: line 9: cut: command not found
I'm getting what I want but I don't understand why the second call to PATH= produces the above error. What am I missing?

PATH is a system variable which the bash shell uses to find where
your binaries(eg cut) are.
So, till :
PATH=$(echo $ST | cut -d'/' -f4-)
things work as expected. But after the command substitution ie $(...), PATH points to a non-standard directory where bash could not find the standard binaries.
So the subsequent command :
PATH=$(echo $ST | cut -d'/' -f4-)
gave you the error :
./nn.sh: line 9: cut: command not found
Moral
Never use uppercase variables for your scripts as they are reserved for the system.

Related

How do you remove a section of of a file name after underscore including the underscore using bash? [duplicate]

How can I remove all text after a character, in this case a colon (":"), in bash? Can I remove the colon, too? I have no idea how to.
In Bash (and ksh, zsh, dash, etc.), you can use parameter expansion with % which will remove characters from the end of the string or # which will remove characters from the beginning of the string. If you use a single one of those characters, the smallest matching string will be removed. If you double the character, the longest will be removed.
$ a='hello:world'
$ b=${a%:*}
$ echo "$b"
hello
$ a='hello:world:of:tomorrow'
$ echo "${a%:*}"
hello:world:of
$ echo "${a%%:*}"
hello
$ echo "${a#*:}"
world:of:tomorrow
$ echo "${a##*:}"
tomorrow
An example might have been useful, but if I understood you correctly, this would work:
echo "Hello: world" | cut -f1 -d":"
This will convert Hello: world into Hello.
$ echo 'hello:world:again' |sed 's/:.*//'
hello
I know some solutions:
# Our mock data:
A=user:mail:password
With awk and pipe:
$ echo $A | awk -v FS=':' '{print $1}'
user
Via bash variables:
$ echo ${A%%:*}
user
With pipe and sed:
$ echo $A | sed 's#:.*##g'
user
With pipe and grep:
$ echo $A | egrep -o '^[^:]+'
user
With pipe and cut:
$ echo $A | cut -f1 -d\:
user
egrep -o '^[^:]*:'
trim off everything after the last instance of ":"
grep -o '^.*:' fileListingPathsAndFiles.txt
and if you wanted to drop that last ":"
grep -o '^.*:' file.txt | sed 's/:$//'
#kp123: you'd want to replace : with / (where the sed colon should be \/)
Let's say you have a path with a file in this format:
/dirA/dirB/dirC/filename.file
Now you only want the path which includes four "/". Type
$ echo "/dirA/dirB/dirC/filename.file" | cut -f1-4 -d"/"
and your output will be
/dirA/dirB/dirC
The advantage of using cut is that you can also cut out the uppest directory as well as the file (in this example), so if you type
$ echo "/dirA/dirB/dirC/filename.file" | cut -f1-3 -d"/"
your output would be
/dirA/dirB
Though you can do the same from the other side of the string, it would not make that much sense in this case as typing
$ echo "/dirA/dirB/dirC/filename.file" | cut -f2-4 -d"/"
results in
dirA/dirB/dirC
In some other cases the last case might also be helpful. Mind that there is no "/" at the beginning of the last output.

How do I parse a cmd line variable into 2-parts?

This is my first post here. Absolute newbie here - plz be gentle :)
(%2=mydomain.ddns.net and mydomain.com into $SUB_DOMAIN . DOMAIN
./test.sh <name> <dydomain.com>
./test.sh <name> <mysub.domain.com>
would both produce the same result.
var1=<name>
var2=<subdomain> # if present
var3=<domain>
url=<$var1.$var2.$var3>
Regardless of the existance of a subdomain or not, I need $url to be complete with
<name>.(subdomain).<domain>
UPDATE: Thanks everyone for prompt responses, I am currently reading through the answers provided and testing. This script will be run remotely on a VPS HOST so I would need the script to fail and exit if there are unbound variables. I could not prompt for user input.
Thank you.
boo="mydomain.ddns.net"
using '-d .' with 'cut' , given string divided in multiple fields with '.' as delimiter.
'-f1' with cut displays only field 1 and '-f2,3' displays 2 and 3 fields.
echo $boo | cut -f1 -d.
mydomain
echo $boo | cut -f2,3 -d.
ddns.net
var1=$(echo $boo | cut -f1 -d.)
var2=$(echo $boo | cut -f2,3 -d.)
echo $var1
mydomain
echo $var2
ddns.net

Bash: Command not found inside for loop

I'm trying to iterate through a list of folder names, and perform some operations on the name, but whatever I try to do inside the while loop, results in a "Command not found".
For example, the following code:
#!/bin/bash
C=$(echo "ABCDEF" | cut -c1)
R=$(echo "ABCDEF" | sed "s/A/X/g")
echo $C
echo $R
for PATH in $(find . -maxdepth 1 -type d); do
C=$(echo $PATH | cut -c1)
R=$(echo $PATH | sed "s/A/X/g")
echo $C
done
Outputs:
A
XBCDEF
line 9: cut: command not found
line 10: sed: command not found
PATH is a special variable that tells the shell where to find common utilities. For instance, sed and cut are usually in /bin and $PATH usually includes /bin.
So, in your for loop, you've redefined $PATH to be the result of your find operation. You'll have better luck if you use a variable name other than PATH.

Parameter expansion to assign environment variable in bash

I want to get the current svn revision using bash and set it as the environment variable SVN_REVISION. This environment variable may or may not be already set. If it's already set then I echo that, if not set then I compute it and then echo it. I DONT'T want to override if SVN_REVISION is already set. I am using the following script which is failing owing to my lack of understanding of parameter expansion.
#!/bin/bash
# If no directory provided as an argument, uses the current working directory as the source directory.
RAW_SRC_DIR=${1:-`pwd`}
COMPUTE_REVISION=$(svn info ${RAW_SRC_DIR} | grep '^Revision' | cut -d ':' -f2 | tr -d ' ')
echo "${COMPUTE_REVISION}" ##Gets the revision successfully
${SVN_REVISION:="$COMPUTE_REVISION"} #Fails with message: <SVN_REVISION> command not found
export SVN_REVISION
echo $SVN_REVISION
How do I fix it?
One of the effects of the ${parameter:=word} syntax is that the value of parameter is substituted. That means your shell is going to try to execute whatever number you get as a command. Just do the assignment in the echo line and put the export afterwards.
echo ${SVN_REVISION:="$COMPUTE_REVISION"}
export SVN_REVISION
Why not just do it the obvious way?
[[ -z $SVN_REVISION ]] && \
SVN_REVISION=$(svn info ${1:-`pwd`} | grep '^Revision' | cut -d ':' -f2 | tr -d ' ')
echo $SVN_REVISION
export SVN_REVISION
or, if you insist
echo ${SVN_REVISION:=$(svn info ${1:-`pwd`} |
grep '^Revision' |
cut -d ':' -f2 |
tr -d ' ')}
export SVN_REVISION

How to handle variables that contain ";"?

I have a configuration file that contains lines like "hallo;welt;" and i want to do a grep on this file.
Whenever i try something like grep "$1;$2" my.config or echo "$1;$2 of even line="$1;$2" my script fails with something like:
: command not found95: line 155: =hallo...
How can i tell bash to ignore ; while evaluating "..." blocks?
EDIT: an example of my code.
# find entry
$line=$(grep "$1;$2;" $PERMISSIONSFILE)
# splitt line
reads=$(echo $line | cut -d';' -f3)
writes=$(echo $line | cut -d';' -f4)
admins=$(echo $line | cut -d';' -f5)
# do some stuff on the permissions
# replace old line with new line
nline="$1;$2;$reads;$writes;$admins"
sed -i "s/$line/$nline/g" $TEMPPERM
my script should be called like this: sh script "table" "a.b.*.>"
EDIT: another, simpler example
$test=$(grep "$1;$2;" temp.authorization.config)
the temp file:
table;pattern;read;write;stuff
the call sh test.sh table pattern results in: : command not foundtable;pattern;read;write;stuff
Don't use $ on the left side of an assignment in bash -- if you do it'll substitute the current value of the variable rather than assigning to it. That is, use:
test=$(grep "$1;$2;" temp.authorization.config)
instead of:
$test=$(grep "$1;$2;" temp.authorization.config)
Edit: also, variable expansions should be in double-quotes unless there's a good reason otherwise. For example, use:
reads=$(echo "$line" | cut -d';' -f3)
instead of:
reads=$(echo $line | cut -d';' -f3)
This doesn't matter for semicolons, but does matter for spaces, wildcards, and a few other things.
A ; inside quotes has no meaning at all for bash. However, if $1 contains a doublequote itself, then you'll end up with
grep "something";$2"
which'll be parsed by bash as two separate commands:
grep "something" ; other"
^---command 1----^ ^----command 2---^
Show please show exactly what your script is doing around the spot the error is occurring, and what data you're feeding into it.
Counter-example:
$ cat file.txt
hello;welt;
hello;world;
hell;welt;
$ cat xx.sh
grep "$1;$2" file.txt
$ bash -x xx.sh hello welt
+ grep 'hello;welt' file.txt
hello;welt;
$
You have not yet classified your problem accurately.
If you try to assign the result of grep to a variable (like I do) your example breaks.
Please show what you mean. Using the same data file as before and doing an assignment, this is the output I get:
$ cat xx.sh
grep "$1;$2" file.txt
output=$(grep "$1;$2" file.txt)
echo "$output"
$ bash -x xx.sh hello welt
+ grep 'hello;welt' file.txt
hello;welt;
++ grep 'hello;welt' file.txt
+ output='hello;welt;'
+ echo 'hello;welt;'
hello;welt;
$
Seems to work for me. It also demonstrates why the question needs an explicit, complete, executable, minimal example so that we can see what the questioner is doing that is different from what people answering the question think is happening.
I see you've provided some sample code:
# find entry
$line=$(grep "$1;$2;" $PERMISSIONSFILE)
# splitt line
reads=$(echo $line | cut -d';' -f3)
writes=$(echo $line | cut -d';' -f4)
admins=$(echo $line | cut -d';' -f5)
The line $line=$(grep ...) is wrong. You should omit the $ before line. Although it is syntactically correct, it means 'assign to the variable whose name is stored in $line the result of the grep command'. That is unlikely to be what you had in mind. It is, occasionally, useful. However, those occasions are few and far between, and only for people who know what they're doing and who can document accurately what they're doing.
For safety if nothing else, I would also enclose the $line values in double quotes in the echo lines. It may not strictly be necessary, but it is simple protective programming.
The changes lead to:
# find entry
line=$(grep "$1;$2;" $PERMISSIONSFILE)
# split line
reads=$( echo "$line" | cut -d';' -f3)
writes=$(echo "$line" | cut -d';' -f4)
admins=$(echo "$line" | cut -d';' -f5)
The rest of your script was fine.
It seems like you are trying to read a semicolon-delimited file, identify a line starting with 'table;pattern;' where table is a string you specify and pettern is a regular expression grep will understand. Once the line is identified you wish to replaced the 3rd, 4th and 5th fields with different data and write the updated line back to the file.
Does this sound correct?
If so, try this code
#!/bin/bash
in_table="$1"
in_pattern="$2"
file="$3"
while IFS=';' read -r -d$'\n' tuple pattern reads writes admins ; do
line=$(cut -d: -f1<<<"$tuple")
table=$(cut -d: -f2<<<"$tuple")
# do some stuff with the variables
# e.g., update the values
reads=1
writes=2
admins=12345
# replace the old line with the new line
sed -i'' -n $line'{i\
'"$table;$pattern;$reads;$writes;$admins"'
;d;}' "$file"
done < <(grep -n '^'"${in_table}"';'"${in_pattern}"';' "${file}")
I chose to update by line number here to avoid problems of unknown characters in the left hand of the substitution.

Resources