Remix.run Logo
eqvinox 17 hours ago

Not sure I see the point of this. Remembering a bunch of options on one tool is no better than a bunch of tools/constructs? Especially if the latter are useful elsewhere. And it can't be used for scripts unless you start shipping it alongside, which… nah.

Just go with

  foo | while read X; do bar "$X"; done
xorcist 15 hours ago | parent | next [-]

Bash while loops are pretty readable and the above would be a nice to iterate over lines if it wasn't for that gnarly pipe, which is a common source of errors in this construct. Remember that a pipe starts a new shell. So:

  grep stuff file.txt | while read key value ; do [ "$key" = "target" ] && found="$value" ; done
where you might expect $found to end up with the value for the line that has "target" in the first column. Then you notice that darn pipe symbol. The variable found is set in a subshell that terminates and the value is lost. This is a problem every time you need to keep some sort of state when looping. If you can tolate a bash-ism then you could do:

  while read key value ; do [ "$key" = "target" ] && found="$value" ; done < <(grep stuff file.txt)
but that doesn't read as nice and isn't compatible. It does avoid a common source of problems though, and might be worth getting into muscle memory for the times it is needed.
eqvinox 10 hours ago | parent | next [-]

You can put the "< <( foo )" before the "while" btw, for pipe-like ordering of things. All redirections can be anywhere on the command line.

dylan604 14 hours ago | parent | prev [-]

> and isn't compatible

is that a GNU v POSIX type of compatibility issue?

eqvinox 10 hours ago | parent [-]

bash vs. POSIX sh (or some other shells)

zsh should be bash compatible on this AFAIR

t43562 9 hours ago | parent | prev | next [-]

While FTW! I just wondered whether it could be simplified because I get a bit tired of typing out my own belt and braces version of it:

  { # code that generates one item/filename per line of output }  | { while read ITEM; do # do something with "$ITEM"; done; }
So I just tried this and it seems to work for a trivial case although you need 1 escape:

  enum() {
    local source=$1; shift; local action=$1; shift  
    { eval "$source" ; } | { while read ITEM; do eval "$action"; done; }
  }

  $ enum "find /tmp" "echo found: \$ITEM"

  found: /tmp/.XIM-unix
  found: /tmp/.ICE-unix
laughing_man 15 hours ago | parent | prev | next [-]

Depending on the actual command, this can be far slower and less efficient than xargs. You're creating a separate process for each invocation of bar when a lot of commands will take many targets for a single invocation.

Try this with find and grep vs xargs. There's a big difference.

eqvinox 10 hours ago | parent [-]

3 reasons against that:

* in a lot of cases the performance just doesn't matter

* xargs gets you the spaces in filenames landmine

* some commands don't even support multiple target filename arguments

For scripts in long term use, yeah, sure, figure out xargs maybe. Any other situation with a "| while read" solution, especially on an interactive session, is an oddball and simplicity wins.

aa-jv 4 hours ago | parent | next [-]

>* xargs gets you the spaces in filenames landmine

Ignorance of xargs gets you the landmine.

Understanding of xargs, helps you complete the mission without blowing off limbs.

    $ find . -name "Some Files With Spaces*.txt" -print0 | xargs -0 -I {} echo "Processing: {}"  # landmine avoided
laughing_man 10 hours ago | parent | prev [-]

xargs supports null termination.

While it's true not every command supports multiple targets, presumably you know if you're using one of those commands.

JoshTriplett 15 hours ago | parent | prev | next [-]

I use "| while read" as well, because it works well in a pipeline and handles embedded spaces. It doesn't handle embedded newlines, but in practice, real files have embedded spaces, while embedded newlines only happen in test cases and exploits. (You can do actual NUL-delimited reads with `-d ''`, but for a quick command-line operation that's generally not necessary, and if you're going to be that careful you probably also need `-r`.)

aa-jv 4 hours ago | parent | prev | next [-]

Yes, I think the author is just showing their ignorance, not actually doing anything about that ignorance, and re-inventing the wheel:

    $ find /path -name "*.pdf" -print0 | xargs -0 -I {} echo "Processing: {}" # handles paths properly, obviates the need to write anything new whatsoever
Seriously kids, learn your tools and check yourself before you wreck yourselves writing tools that really, really don't need to be written.
fiddlerwoaroof 15 hours ago | parent | prev [-]

I just use while’s default variable name, $REPLY most of the time. But `while` is my preferred tool for the xargs problem in most cases.