13

I have ran into a problem trying to write a Bash script. When grep outputs, it returns (usually) many lines. I would like to prefix and suffix a string to each of these output lines.

I would also like to note that I'm piping ls into grep, like:

ls | grep
  • 2
    You shouldn't parse ls - it's bad juju. Also, does it have to be grep? And can you give some example output? – Sobrique Dec 24 '15 at 19:50
  • 1
    What do you want your script to do if the filename contains a newline? – Tai Viinikka Dec 24 '15 at 20:57
20

With sed:

ls | grep txt | sed 's/.*/prefix&suffix/'
  • 2
    Or, sed being a superset of grep: ls | sed -n '/txt/s/.*/prefix&suffix/p' or ls | sed -n 's/.*txt.*/prefix&suffix/p' – Stéphane Chazelas Dec 24 '15 at 22:44
  • @StéphaneChazelas: Thank you for this hint. – Cyrus Dec 24 '15 at 23:22
5

With sed:

ls | grep pattern | sed -e 's/^/prefix/' -e 's/$/suffix/'

But note, this suffers from problems with filenames with line feeds, and all the assorted problems of parsing ls which is generally a bad idea.

With perl

perl -e 'print "prefix".$_."suffix\n" for grep { m/somepattern/} glob "*"'

Note - perl grep can take a pattern - like the grep command, but you can also do things like apply file tests.

E.g.

grep {-d} glob "*" #filters directories.
grep { (stat)[9] > time() - 60 } glob "*" #reads file mtime and compares. 

Within grep the default iterator $_ is set to the current element value, so you can apply sed/grep style regex, or perform a variety of tasks based on $_.

  • How would I include a variable as a suffix/prefix in your first example using ´sed´? – SpecialBomb Dec 24 '15 at 23:35
  • Change the quotes to and just use em. – Sobrique Dec 25 '15 at 9:55
2

In this case GNU find lets you do all these things in one go, eliminating pipes and potentially troublesome parsing of ls:

find . -maxdepth 1 -name '[^.]*' \
  -regextype posix-extended -regex "MYPATTERN" \
  -printf 'SOMEPREFIX %f SOMESUFFIX\n'

(find is not a good way to arbitrarily modify the output of other commands, of course!)

Some notes:

  • -maxdepth 1 and -name [^.]* make name matching work the same as a plain ls (or ls .). You can use any shell-style glob, but note that "*" will match a leading "." in a name unlike bash, so [^.]* means anything that doesn't have a leading "."
  • MYPATTERN is a proper POSIX ERE (default type is Emacs, see here), but it must match the entire filename so use something like .*thing.* instead of just thing
  • you can probably just use one of -name/-regex instead of both (e.g. -regex "[^.].MYPATTERN."
  • -printf supports lots of things, %n is the unadorned file or directory name

(this can depend on your version of find though, check the "STANDARDS CONFORMANCE" section of your man page)

As a possible alternative with no external programs required, bash has compgen which, among other things, expands globs, equivalent to an ls with no options:

compgen -P "someprefix>" -S "<somesuffix" -G "pattern"

This handles filenames with whitespace, including newlines. compgen -G "*" should provide the same output as a plain ls (but note that ls * is a different thing entirely). You'll still need grep, and this may or may not solve the problem, but it's worth a mention.

0

sed and find solutions are challenged if your prefix/suffix include regex key characters or if want to pass an environment variable (instead of an immediate value), xargs+echo might be more appropriate here.

With environment variable:

ls | grep p | xargs -I % sh -c "echo $PATH/%"

With an inline:

ls | grep p | xargs -I % sh -c "echo `pwd`/%"

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.