2

Suppose, the result of a command in linux shell is as follows:

X and Y are friends

Is there any way to slice each of the words (X, and, Y, are, friends) or the first n words from the result, so that they can be used for different operations?

2

How about cut?

$ phrase="X and Y are friends"
$ cut -d " " -f 1 <<< $phrase
X
$ cut -d " " -f 2 <<< $phrase
and
$ cut -d " " -f 3 <<< $phrase
Y
$ cut -d " " -f 4 <<< $phrase
are
$ cut -d " " -f 5 <<< $phrase
friends

The -d specifies the delimiter (a space) and the -f the field number (fields being separated by delimiters).

I've place the string in a variable in the example above, but you could pipe from the output of a command:

$ mycommand | cut -d " " -f 2
and    
1

You can also di this directly in the shell using read:

$ echo "X and Y are friends" | 
  while read a b c d e f
     do echo "a is '$a', b is '$b', c is '$c', d is '$d', e is '$e', f is '$f'"
  done
a is 'X', b is 'and', c is 'Y', d is 'are', e is 'friends', f is ''

The default delimiter is whitespace but you can set it to something else by changing the IFS variable:

$ echo "foo:bar" | while IFS=: read  a b; do echo "a is '$a', b is '$b'"; done
a is 'foo', b is 'bar'
1

There are two main ways to capture the output of a command in a shell script: command substitution and the read builtin.

The simple way to split the output into words is to rely on the shell's built-in splitting feature, and put the output into an array:

words=($(echo "X and Y are friends"))
echo "The ${words[5]} are ${words[1]} and ${words[3]}"

This works in shells with arrays: ksh93¹, mksh, bash, zsh. In other shells, you can't store a list of words except in the positional parameters.

set -- $(echo "X and Y are friends")
echo "The $5 are $1 and $3"

Actually, each word in the output is treated as a wildcard pattern, and replaced by the list of matching files if any. (Except in zsh, which only does this when explicitly instructed unless in sh compatibility mode.) For example, if one of the words is *, it'll be replaced by the list of files in the current directory. To avoid this, turn off wildcard matching:

set -f
words=($(echo "* and / are punctuation"))
echo "Here's some ${words[5]}: ${words[1]} and ${words[3]}"
set +f

With read, you can assign individual words to a variable each. The tricky part about read is that since it reads from standard input, it's often used as the right-hand side in a pipe; but in most shells (ATT ksh and zsh excepted), both sides of a pipe run in a subshell, so the variable assignments are lost outside the pipe. You can put read as part of a sequence of instructions.

echo "X and Y are friends" | {
  read -r first conjunction second verb complement remainder
  echo "The $complement are $first and $second"
}

Alternatively, in ksh93, bash or zsh, you can pass the input in a process substitution.

read -r first conjunction second verb complement remainder <(echo "X and Y are friends")
echo "The $complement are $first and $second"

If you want to store the words in an array, you can use read -rA words in mksh, ksh93 and zsh, or read -ra words in bash, e.g. in bash

read -ra words <(echo "X and Y are friends")

is equivalent to

set -f; words=$((echo "X and Y are friends")); set +f

if the command outputs a single line, except that it doesn't reset the -f option if it was on before.

¹ Ksh88 has arrays but assignment uses a different syntax.

0

With zsh:

upToFirst5words=(${$(my-cmd)[1,5]})

Assuming the default the default value of IFS, that will split on sequences blanks (space, tab, newline), or NULs.

You can make it:

argv=(${$(my-cmd)[1,5]})

For those 5 words to be in $1, $2... $5.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.