1

I'm trying to set a variable in bash:

b=$(p | cut -d',' -f4)
echo $b

However, I keep ending up with:

p: command not found

I've tried every combination of brackets, curly brackets, and dollar signs but still can't get b to equal $p | cut -d',' -f4. To my understanding, the dollar signs will extract the value from the variable and the brackets are for expression containment. Is there fundamental bash syntax that I'm not aware of?

4
  • What is p? and what exactly are you even trying to do? Commented Jan 28, 2017 at 6:49
  • p is a variable that I've obtained through a for loop. Commented Jan 28, 2017 at 6:51
  • Well p isn't a command, so why try and run it in a subshell like one? I think you'll want an echo in front of it, for example: b=$(echo "$p" | ... )... Commented Jan 28, 2017 at 6:51
  • Try b=$(echo "$p" | cut -d',' -f4) Commented Jan 28, 2017 at 6:53

4 Answers 4

1

How about using Bash patterns for this?

  • With parameter expansions (super portable):

    b=${p#*,*,*,}
    b=${b%%,*}
    
  • By splitting the comma-separated string and reading only the fourth field (Bash only):

    IFS=, read -r -d '' _ _ _ b _ < <(printf '%s,\0' "$p")
    
  • By using a regex (that will also provide some sort of validation, Bash only):

    if [[ $p =~ ^[^,]*,[^,]*,[^,]*,([^,]*).*$ ]]; then
        b=${BASH_REMATCH[1]}
    else
        echo >&2 "String doesn't match"
    fi
    
  • By fully splitting the string into an array (same as point 2 above, Bash only):

    IFS=, read -r -d '' -a pary < <(printf '%s,\0' "$p")
    b=${pary[3]}
    
Sign up to request clarification or add additional context in comments.

5 Comments

You use zero-delimited string to feed into read. Is that to allow newlines inside the field values? You may want to explain the reason, as I am sure it is useful. Maybe it can be avoided sometimes, for instance if p contains individual lines from a file that were read using newlines as line separators. The parameter expansion I did not think of, certainly the fastest and most concise solution in this case.
@Fred: I'm not using a null-delimited string. I'm using read with a null delimiter -d '' and the \0 in the printf format is only to have read return 0 (success); without \0, read would return 1. The construct I'm using allows newlines in the string. It's the only robust way I know of to split a string on delimiters (apart from parsing it with a loop).
Would it be valid to say that if the string can be assumed to contain no newlines, then -d '' can be dropped and <<<"$p" used instead of the printf process substitution?
About the null-delimited string... Wouldn't printf "%s\0" produce a string terminated by a null byte?
@Fred: if you're sure that p doesn't contain newlines characters (or, if it does, you only want to split the first line), then it's fine to use <<< "$p" (and it's much faster too). Regarding the format %s,\0, the comma is here to allow empty trailing fields. Compare p=hello,word, IFS=, read -r -d '' -a ary < <(printf '%s\0' "$p"); declare -p ary and p=hello,word, IFS=, read -r -d '' -a ary < <(printf '%s,\0' "$p"); declare -p ary.
1

The code below would also work, and would probably be higher performance by avoiding a call to the external program cut and using only the read builtin :

IFS="," read -r dummy dummy dummy b dummy  <<<"$p"
echo "$b"

You could also collect all values in an array, and reference them afterwards

IFS="," read -r -a array <<<"$p"
echo "${array[4]}"              # Print 4th field
echo "${#array[@]}"             # Print field count

In all cases, IFS="," is how you tell read to use commas as field separators, and -r forces backslashes to be used as is, not interpreted (e.g. \n will not be seen as a newline, but rather just as a literal two-character string).

Please note that the examples above only handle strings that do not contain newlines.

Comments

0

Use a here-string to expand the variable value to p to cut instead of using a pipe-line(|) which forks a new sub-shell (avoiding a new process overhead). The <<< is bash specific and not available in the POSIX shell sh.

b=$(cut -d',' -f4 <<<"$p")
echo "$b"

Also double-quote your variables to avoid word-splitting by shell.

Comments

0

If p is a variable then try this:

b=$(echo "$p" | cut -d',' -f4)
echo $b

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.