0

I need to pass a string containing slashes to sed to extract some values from it, but when I try to do so it treats it as a directory/file and throws a "No such directory or file" error instead. I'm assuming this is because it reads it as a path and tries to find the file when it is not the intended effect. Example:

my_value="my/value"

operand="$($my_value | sed -n -e 's/^\(.*\)\/.*/\1/p')"

# Expected echo $operand : my

Note: I am new to bash scripts so this could be the wrong way to go about this

3 Answers3

2

If you want the output of echo $operand to be my, then your syntax is fine except for one thing that's missing:

operand="$(echo $my_value | sed -n -e 's/^\(.*\)\/.*/\1/p')"

You need to use echo on the my_value variable so that its value is sent to stdout to have sed operate on it. You are getting the error because there is, in fact, no file or directory called my/value which is the value of your variable.

The above command substitution for the operand variable including echo will output my when using echo $operand.

Another way to do it is with:

operand="$(printf '%s\n' "$my_value" | sed -n -e 's/^\(.*\)\/.*/\1/p')"

This is one of the cases where printf is better so that echo doesn't operate on other strings that are included. It doesn't happen in this case but it might in others.

Nasir Riley
  • 11,422
1

A pipeline as you have constructed relies on the output of one command to be input for another. You are trying to execute a file called my/value and presumably getting told there is no such file.

There are multiple ways to do what you want.

This method, still using a pipeline, is probably most portable.

operand="$(echo "$my_value" | sed -n -e 's/^\(.*\)\/.*/\1/p')"

If your shell supports it, this is an alternative (should work in bash at least):

operand="$(sed -n -e 's/^\(.*\)\/.*/\1/p' <<< "$my_value")"

This is latter method is marginally preferable because it doesn’t invoke an additional process to display the variable value.

In either case, it’s good practise to protect the variable contents with double quotes.

bxm
  • 4,855
1

As others have pointed out, you are trying to execute the value of $my_value as a command in your command substitution.

If you want to strip off everything after the first / in a string, use a command substitution instead:

my_value="this/is/my/value"
operand=${my_value%%/*}

printf '%s\n' "$operand"    # will output the string "this"

The parameter expansion ${my_value%%/*} will result in the longest suffix string matching /* being removed from the value $my_value. This is a standard parameter expansion and would be much quicker for the shell to execute than to call sed.

Also, if the value in $my_value is a pathname, then it would be hazardous to use sed for manipulating it, since it may technically contain newlines. Using a parameter expansion in the shell would handle the case of a pathname containing newlines.

Kusalananda
  • 333,661