59

I have been told it was possible, but without a single working example, that sed can read from a string variable without need for an input file. I have yet to get it to work. For general safety, I’m writing the $PATH variable to another variable, as I mess with this, because I don’t need other problems to arise until I know exactly how to do this.

Consider the following:

~$x=$PATH
~$sed -i 's/:/ /g' $x

this fails with: No such file or directory.

Here are some others I have tried:

~$ sed -i 's/:/ /g' | (read x) 
sed: no input files
~$ sed -i 's/:/ /g' | (read $x) 
bash: read: `/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games': not a valid identifier
sed: no input files
~$ sed -i 's/:/ /g' | $(read x) 
sed: no input files
~$ sed -i 's/:/ /g' | $(read $x) 
bash: read: `/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games': not a valid identifier
sed: no input files
~$ sed -i 's/:/ /g' < $(read $x) 
bash: read: `/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games': not a valid identifier
bash: $(read $x): ambiguous redirect
~$ sed -i 's/:/ /g' < read $x 
bash: read: No such file or directory
~$ sed -i 's/:/ /g' < (read x) 
bash: syntax error near unexpected token `('
~$ sed -i 's/:/ /g' < $((read x)) 
bash: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games: syntax error: operand expected (error token is "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games")
~$ sed -i 's/:/ /g' < $((read $x)) 
bash: read /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games: division by 0 (error token is "usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games")
~$ sed -i 's/:/ /g' << $((read $x)) 
> ;^C
~$ sed -i 's/:/ /g' << $(read $x)
> ;^C
~$ sed -i 's/:/ /g' $x
sed: can't read /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games: No such file or directory
~$ sed -i 's/:/ /g' < $x
bash: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games: No such file or directory
~$ sed -i 's/:/ /g' < echo $x
bash: echo: No such file or directory
~$ sed -i 's/:/ /g' |  echo $x
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
sed: no input files

Can this even work? I would prefer not to need to write files I don't need just so I can use sed. For this particular example, if ~$x=$PATH ~$sed -i 's/:/ /g' $x actually worked the way I would have hoped, I would get:

/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin /usr/games /usr/local/games

which I could then assign to a variable, and use in future commands, like ls $x

1
  • 4
    x=$(echo "$x" | sed 's/:/ /g')?
    – heemayl
    Mar 10, 2015 at 21:29

2 Answers 2

63

This has nothing to do with sed as such, it applies to any program that reads standard input. Anyway, what you were looking for is

sed 's/:/ /g' <<<"$PATH"

There's no reason to save in another variable, nothing you do this way will affect the value stored in the variable. You need the assignment operator (=) for that. The syntax used above is called a "here string" and is specific to bash, ksh and zsh. It serves to pass a variable's value as input to a program that reads from standard input.

I have written up an answer on U&L that lists all the various shell operators like this one. You might want to have a look.

Note that you could also have done

echo "$PATH" | sed 's/:/ /g' 

Finally, you really don't need sed at all for this. You can do the whole thing in bash directly:

echo "${PATH//:/ }"

The above is a bash replacement construct. Given a variable $var, a pattern pat and a replacement (rep), to replace all occurrences of pat with rep, you would do

echo "${var//pat/rep}"

The same thing with a single slash replaces only the first occurrence:

echo "${var/pat/rep}"
4
  • 2
    Thanks for 'bash replacement construct'. I was able to use: "${1/from/to}" in a Bash function to alter the first argument into subsequent parameters. Nov 14, 2017 at 23:31
  • 2
    For future Googlers, this is sometimes called "pattern substitution" and lets you search and replace a pattern (string) that exists within a bash variable. This page has a lot of useful examples: shellscript.sh/tips/pattern-substitution May 22, 2021 at 12:13
  • @JesseNickles for what it's worth, that page doesn't seem very good. It is using CAPS for shell variables which is bad practice (by convention, environment variables are in caps so using caps for your own variables can lead to variable name collision and hard to debug issues) and uses all variables unquoted with is really bad practice.
    – terdon
    May 22, 2021 at 12:22
  • @terdon I have always used CAPS for bash variables myself, idk maybe because I define them in a config/functions script. Anyway you're right about the lack of quotation marks... but linked examples can be replaced with: "${VARIABLE}" May 23, 2021 at 8:10
29

May be:

~$ x="$PATH"
~$ x="$(echo $x | sed 's/:/ /g')"
~$ echo "$x"
/usr/local/bin /usr/bin /bin /usr/local/games /usr/games
4
  • @jOh --- notice that sed here is not working on the "string variable" (?), but on the standard input --- which is how it should be.
    – Rmano
    Mar 10, 2015 at 21:47
  • i think Ill make a feature request to the sed devs
    – j0h
    Mar 10, 2015 at 22:29
  • 4
    @j0h this has nothing to do with sed, it is a question of how the shell redirects input. sed deals with input streams and it reads them through its standard input. Whether they be files or not is irrelevant. It can accept anything as long as it comes through standard input.
    – terdon
    Mar 10, 2015 at 22:48
  • This solution stores a variable which the OP seems to desire. It can be further shortened using "here string" format as described by @terdon, eg: x="$(sed 's/:/ /g' <<<"$x")"
    – David Cook
    Jan 28, 2022 at 5:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .