0

When I run a grep pipeline directly I get two lines of output:

$ grep Interface /proc/net/bonding/bond0 | cut -d : -f 2 | sed 's/ //g'
eno1np0
eno2np1
$

Strangely, the output is empty when I capture it and call echo:

$ echo $(grep Interface /proc/net/bonding/bond0 | cut -d : -f 2 | sed 's/ //g')
              
$

A for loop through grep's output shows many empty lines:

$ for a in $(grep Interface /proc/net/bonding/bond0 | cut -d : -f 2 | sed 's/ //g'); do echo $a; done
 
 
 
 
 
 
 
 
 
 
 
 
 
 
$

For what it's worth, I have tried redirecting stderr to stdout with no luck:

echo $(grep Interface /proc/net/bonding/bond0 2>&1 | cut -d : -f 2 | sed 's/ //g')

I even tried using cat instead of grep.

How can I get echo to actually print what's being output by the grep command? Normally, echo works. I can echo output just I like normally would, for other things. I can echo text into a file. I just can't get it to work with this grep pipeline.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
Sagar
  • 9,456
  • 6
  • 54
  • 96
  • Drop the backslashes before double quotes. – choroba Jun 15 '22 at 12:22
  • The backslashes there are just to demonstrate that it's only outputting spaces. The quotes do not actually do anything to change the output – Sagar Jun 15 '22 at 12:28
  • 1
    Instead of using a `/proc` device, which can have different content every time it's read, can you reproduce this with input coming from a real file? That way people who aren't you (and don't have your specific network configuration) have some chance of reproducing this themselves. – Charles Duffy Jun 15 '22 at 12:42
  • 1
    BTW, while it's not relevant to the specific problem you're encountering, the advice in [DontReadLinesWithFor](https://mywiki.wooledge.org/DontReadLinesWithFor) generally applies. – Charles Duffy Jun 15 '22 at 12:45
  • 1
    [bash warning: command substitution ignored null byte in input](https://stackoverflow.com/questions/64550414/bash-warning-command-substitution-ignored-null-byte-in-input) is a question about how newer versions of bash deal with this same problem. – Charles Duffy Jun 15 '22 at 12:56
  • 3
    providing the output of `xxd /proc/net/bonding/bond0` could help – Fravadona Jun 15 '22 at 13:02
  • @CharlesDuffy well, you can revert the hexdump with `xxd -r`; IMO the advantage of `xxd` over `base64` would be that you get something "meaningful/readable" directly in the question. – Fravadona Jun 15 '22 at 13:44
  • 1
    @Fravadona, nice, thank you -- I wasn't aware `xxd -r` existed. – Charles Duffy Jun 15 '22 at 14:41

2 Answers2

3

My hunch is that the results from /proc/net actually contain null bytes, which throw off the shell.

What you are trying is quite similar to the infamous useless use of echo though I suppose if the goal is to get all the output on a single line, it's not entirely useless (but still broken, because the shell will expand any wildcards in the results).

Perhaps try this:

grep Interface /proc/net/bonding/bond0 |
tr '\000' '\012' |
sed 's/^[^:]*://;s/ //g;N;s/^[^:]*://;s/ //g;s/\n/ /'

though the s/\n/ / thing is probably not portable to all sed variants.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • More modern bash releases ignore NUL bytes in command substitution output, but I have no idea which version RockyLinux 8 uses; it could well be old enough to silently truncate. – Charles Duffy Jun 15 '22 at 12:46
  • @tripleee that almost worked, except the output is a little incorrect (`eno1np0 SlaveInterface:eno2np1`). I can figure out what I need to change, though. I'll look at the link in your answer, thanks. I'm decently in bash, but this one is new to me – Sagar Jun 15 '22 at 12:52
  • 1
    I updated to do another `s/^[^:]*://` before the `N` – tripleee Jun 15 '22 at 12:54
1

Figured this out, based on information from @CharlesDuffy and @tripleee, and a colleague of mine.

I am not sure it's a common occurrence, but posting here in case someone else runs into this in the future.

The cause was that the environment variable IFS was set to a non-standard character. While cat and grep were working correctly - presumably because neither depends on IFS to work - echo was not.

I am not entirely sure how this happened, but for anyone that faces this issue, check your environment for a variable named IFS (env | grep IFS), and set it back to the default (' \t\n').

PS: bad start to a mid-week morning /facepalm

Sagar
  • 9,456
  • 6
  • 54
  • 96
  • 2
    Lack of quoting strikes again. First rule of bash, always quote expansions: `echo "$(grep Interface /proc/net/bonding/bond0 | cut -d : -f 2 | sed 's/ //g')"`. Unquoted expansions are subject to word splitting, which is what burned you here. – John Kugelman Jun 15 '22 at 14:08
  • 1
    Second rule of bash, don't change `IFS` globally. When you do need to change it, prepend it to the command that needs it so it's only set temporarily for that one command. For example, `IFS=',' read a b c` is a safe way to read comma-separated fields as `IFS=','` doesn't persist past that single `read` command. – John Kugelman Jun 15 '22 at 14:10
  • Or if you need it to last for longer, use a subshell, like `foo; (IFS=','; bar; baz); quux`. `IFS=','` affects `bar` and `baz` but not `quux`. – John Kugelman Jun 15 '22 at 14:13
  • You may see old school scripts do `OLDIFS=$IFS; IFS=','; bar; baz; IFS=$OLDIFS`, saving and restoring `IFS`. It's a unsophisticated trick. The other two ways are more elegant and more foolproof. – John Kugelman Jun 15 '22 at 14:15
  • 2
    To be clear, it's not `echo` itself that looks at IFS, but the shell's process of assembling a command line from an unquoted expansion. That process is identical for `grep` or `cat` as well, the reason they weren't burned is that you were passing them filenames instead of content. – Charles Duffy Jun 15 '22 at 14:44
  • 1
    BTW, to return IFS to defaults, run `IFS=$' \t\n'` -- the default is a three-character sequence, rather than the unset state. (The unset state acts a lot like the default, but it's not quite identical; in particular, it can lead to surprises from programs that try to manage IFS's value explicitly). – Charles Duffy Jun 15 '22 at 14:45
  • Thanks @CharlesDuffy - updated to set to default, rather than unset, based on your comment – Sagar Jun 15 '22 at 14:51