Bash Pipe – Why Pipe Doesn’t Work with upower -e

bashpipexargs

I've just found this command:

upower -e

that displays a list of files that can be used with upower -i to display plugged device status.

So my first try was using:

upower -e | xargs upower -i

but it doesn't work. So I've tried:

$ upower -e | xargs echo
/org/freedesktop/UPower/devices/line_power_AC /org/freedesktop/UPower/devices/battery_BAT0 /org/freedesktop/UPower/devices/line_power_ucsi_source_psy_USBC000o001 /org/freedesktop/UPower/devices/DisplayDevice

and it display all files in single line. So I've used:

$ upower -e | xargs -0 echo
/org/freedesktop/UPower/devices/line_power_AC
/org/freedesktop/UPower/devices/battery_BAT0
/org/freedesktop/UPower/devices/line_power_ucsi_source_psy_USBC000o001
/org/freedesktop/UPower/devices/DisplayDevice

it works but displays one empty line, but this doesn't work:

$ upower -e | xargs -0 upower -i
failed to set path: Object path invalid: /org/freedesktop/UPower/devices/line_power_AC
/org/freedesktop/UPower/devices/battery_BAT0
/org/freedesktop/UPower/devices/line_power_ucsi_source_psy_USBC000o001
/org/freedesktop/UPower/devices/DisplayDevice

Why upower -e | xargs upower -i doesn't work? I'm using Bash on Fedora.

Is there something I'm missing here?

EDIT:

This seems to work:

upower -e | xargs -I {} upower -i "{}"

But I'm wondering: why a quote is needed if the filename doesn't have spaces?

Best Answer

upower -e produces a newline-separated list of object paths.

When you used upower -e | xargs upower -i, the xargs command tokenized that on whitespace and passed all the paths to a single invocation of upower -i, which it was unable to handle.

You then tried upower -e | xargs echo, and noted that the output consisted of a single line - that's down to echo though, and does not tell you how the output of upower -e was delimited.

Next you tried upower -e | xargs -0 echo, which (since the output of upower -e is not null delimited) passed a single multi-line argument to echo, which happily printed it. Similarly upower -e | xargs -0 upower -i passed a single multi-line path to upower -i, which unsurprisingly barfed.

Finally you discovered upower -e | xargs -I {} upower -i "{}". Since -I implies -L, this passed each line of the upower -e output to a separate invocation of upower -i. You could have achieved the same with

upower -e | xargs -L upower -i

Whitespace within the paths isn't a factor here, but if it was, you should tell xargs not only to read a single line per invocation, but also to tokenize it on newlines only:

upower -e | xargs -d '\n' -L upower -i

or equivalently

upower -e | xargs -d '\n' -n 1 upower -i

Quoting the replacement text {} probably isn't necessary either (regardless of whether the text itself contains whitespace) - see Quoting curly braces in the shell and the linked duplicate discussing the same issue in the context of find -exec

Related Question