16

The grep command gives out an exit status:

$echo "foo.bar" | grep -o foo
foo
$echo $?
0
$echo "foo.bar" | grep -o pop
$echo $?
1  

But I need to use sed and I realized that it has no exit status:

$echo "foo.bar" | sed 's/bar.*$//'
foo.
$echo $?
0
$echo "foo.bar" | sed 's/pop.*$//'
foo.bar
$echo $?
0  

I know that I should play around with the -q option, but I have not succeeded.

K7AAY
  • 17,735
Josef Klimuk
  • 1,636
  • 8
    Note that sed most certainly does have an exit status, it just doesn't do what you need here. If the sed command fails, for example, if you try to run it on a file you don't have write access to or one that doesn't exist, sed will exit with a non-0 exit status. The exit status just indicates whether sed managed to do what you told it to do, and echo "foo.bar" | sed 's/pop.*$//' was correctly executed. It deleted all lines with pop. That there were no such lines is irrelevant, the command still worked. – terdon May 16 '18 at 13:20
  • @Zanna I was going to post an answer, but I felt this doesn't actually answer the question. It only explains a misconception, but doesn't provide a helpful solution like steeldriver has below. So a comment seemed more appropriate. – terdon May 16 '18 at 15:17

3 Answers3

16

You can use qn to quit with exit status n - but to make that useful, you will also need to use some Branching and Flow Control:

t
branch conditionally (that is: jump to a label) only if a s/// command has succeeded since the last input line was read or another conditional branch was taken.

It is probably best to choose a value for n that is distinct from one of the standard exit status values:

An exit status of zero indicates success, and a nonzero value indicates failure. GNU 'sed' returns the following exit status error values:

0
 Successful completion.

1
 Invalid command, invalid syntax, invalid regular expression or a
 GNU 'sed' extension command used with '--posix'.

2
 One or more of the input file specified on the command line could
 not be opened (e.g.  if a file is not found, or read permission is
 denied).  Processing continued with other files.

4
 An I/O error, or a serious processing error during runtime, GNU
 'sed' aborted immediately.

So for example

$ echo "foo.bar" | sed 's/bar.*$//; t; q42' ; echo $? 
foo.
0

whereas

$ echo "foo.bar" | sed 's/baz.*$//; t; q42' ; echo $? 
foo.bar
42

If you want to omit the default printing of the pattern space, then replace q by Q (note that Q is a GNU extension).

steeldriver
  • 143,099
  • 8
    Doesn't work for multiline file sed -i where replacement is somewhere i middle, only works for single-line patterns. Any idea how to use in a multiline file scenario? – lonix May 26 '21 at 07:18
  • This also mysteriously mangles files. For example: echo $'foo\nbar\nhello\nworld' >testfile, then sed -i -E 's/hello/bye/; t; q1' testfile. Only foo will remain in the file, and the return code is 1. Can anyone explain what's going on? – what the May 15 '24 at 17:37
  • 2
    @whatthe since sed is line-based by default, the t test will fail on the first line that doesn't contain hello, triggering the q1. Moreover -i truncates and re-writes the file, and nothing gets written after the quit instruction. Depending on your specific use case, you may be able to make it work the way you want by adding the -z/--null-data option so that sed reads the whole input as one. – steeldriver May 15 '24 at 20:17
1

Here's how to search regex with sed, and highlight matches, or return exit code (5) if no match found:

This is input.txt:

hello there
my dear old friend
a lot of things
we'll pass along the end

Here's my function to Print all + Highlight matches + Return exit code:

highlight()
{
  pattern=$1
  shift
  sed '/'"${pattern}"'/,${s//\x1b[32m&\x1b[0m/g;b};$q5' "$@"
}

$ highlight "lo\|end" input.txt || echo -e "\n* No match found *"

hello there
my dear old friend
a lot of things
we'll pass along the end

When there's no match, it will return exit code (5). You can use it with cat and pipe | as well:

$ cat input.txt | highlight "hot\|and" || echo -e "\n* No match found *"

hello there
my dear old friend
a lot of things
we'll pass along the end

* No match found *

Thanks to https://unix.stackexchange.com/a/405725/43233 - I'm using it + sed quit option.

0

duplicate of answer https://stackoverflow.com/a/61808364/10440128

#!/bin/sh

read -d '' input <<EOF a b EOF

echo "replace yes:" echo "$input" >input.txt sed -i 's/a/x/ w /dev/stdout' input.txt

echo "replace no:" echo "$input" >input.txt sed -i 's/z/x/ w /dev/stdout' input.txt

output:

replace yes:
x
replace no:

so, to check if sed did replace something:

#!/bin/sh

read -d '' input <<EOF a b EOF

echo "$input" >input.txt if [ -z "$(sed -i 's/a/x/w /dev/stdout' input.txt)" ] then echo "replace no" # -z = "zero string" else echo "replace yes" fi

echo "$input" >input.txt if [ -n "$(sed -i 's/z/x/w /dev/stdout' input.txt)" ] then echo "replace yes" # -n = "nonzero string" else echo "replace no" fi

output:

replace yes
replace no

limitation: can handle only one substitution. so this does not work:

sed -i 's,a,b, w /dev/stdout ; s,b,a, w /dev/stdout' input.txt

error:

sed: couldn't open file /dev/stdout ; s,b,a, w /dev/stdout: No such file or directory

(sed treats everything after w as a file path)

workaround: use multiple sed calls

#!/bin/sh

read -d '' input <<EOF a b EOF

echo "$input" >input.txt if [ -n "$( sed -i 's/a/x/w /dev/stdout' input.txt && sed -i 's/x/a/w /dev/stdout' input.txt )" ] then echo "replace yes" else echo "replace no" fi

milahu
  • 376