Skip Navigation

Very simple foreach line alias to xargs - is it usefule?

I created a simple alias for xargs, with the intend to pipe it when needed. It will simply run a command for each line of it. My question to you is, is this useful or are there better ways of doing this? This is just a little bit of brainstorming basically. Maybe I have a knot in my head.

 undefined
    
# Pipe each line and execute a command. The "{}" will be replaced by the line.
# Example:
#   find . -maxdepth 2 -type f -name 'M*' | foreach grep "USB" {}
alias foreach='xargs -d "\n" -I{}'


  

For commands that already operate on every line from stdin, this won't be much useful. But in other cases, it might be. A more simplified usage example (and a useless one) would be:

 undefined
    
find . -maxdepth 1 | foreach echo "File" {}

  

It's important to use the {} as a placeholder for the "current line" that is processed. What do you think about the usefulness? Have you any idea how to use it?

You're viewing a single thread.

15 comments
  • Don't use ls if you want to get filenames, it does a bunch of stuff to them. Use a shell glob or find.

    Also, because filenames can have newlines, if you want to loop over them, it's best to use one these:

     undefined
        
    for x in *; do do_stuff "$x"; done # can be any shell glob, not just *
    find . -exec do_stuff {} \;
    find . -print0 | xargs -0 do_stuff # not POSIX but widely supported
    find . -print0 | xargs -0 -n1 do_stuff # same, but for single arg command
    
      

    When reading newline-delimited stuff with while read, you want to use:

     undefined
        
    cat filenames.txt | while IFS= read -r x; do_stuff "$x"; done
    
      

    The IFS= prevents trimming of whitespace, and -r prevents interpretation of backslashes.

    • Those find and ls commands were just to illustrate how the actual alias work, to get a sense of. I'm aware of those issues with filenames. It's not about ls or find here.

    • Some additional thoughts to be aware of by looking closer to each line (previously I just glanced over).

      This point is not directly affecting your example, but I want to make you aware of something I fall into myself. Its one of those Bash quirks. Other shells might handle it differently, only speaking about Bash here. For a regular for loop over files, its important to note that if no file exists, the variable will be set to the search string. So example for x in *.png; do, if no .png file is found, then x will be set to *.png literally. So depending on what you do in the loop this could be catastrophic. But Bash has an option for this specifically: shopt -s nullglob . Using this option, if no file is found, then x will be set to an empty string. More about Bash options: https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html

       undefined
          
      for x in *.abcdefg; do echo "$x"; done
      shopt -s nullglob
      for x in *.abcdefg; do echo "$x"; done
      
        

      BTW one can also do a read line by line without cat, by reading the file directly: (for some reasons Beehaw won't let me type the lower than character, so replace that, here a screenshot too):

       undefined
          
      while IFS= read -r line; do echo "Line: ${line}" ; done \< filenames.txt
      
      
        
15 comments