Make xargs handle filenames that contain spaces

前端 未结 12 1155
北恋
北恋 2020-11-29 15:21
$ ls *mp3 | xargs mplayer  

Playing Lemon.  
File not found: \'Lemon\'  
Playing Tree.mp3.  
File not found: \'Tree.mp3\'  

Exiting... (End of file)  
12条回答
  •  野趣味
    野趣味 (楼主)
    2020-11-29 15:30

    I know that I'm not answering the xargs question directly but it's worth mentioning find's -exec option.

    Given the following file system:

    [root@localhost bokeh]# tree --charset assci bands
    bands
    |-- Dream\ Theater
    |-- King's\ X
    |-- Megadeth
    `-- Rush
    
    0 directories, 4 files
    

    The find command can be made to handle the space in Dream Theater and King's X. So, to find the drummers of each band using grep:

    [root@localhost]# find bands/ -type f -exec grep Drums {} +
    bands/Dream Theater:Drums:Mike Mangini
    bands/Rush:Drums: Neil Peart
    bands/King's X:Drums:Jerry Gaskill
    bands/Megadeth:Drums:Dirk Verbeuren
    

    In the -exec option {} stands for the filename including path. Note that you don't have to escape it or put it in quotes.

    The difference between -exec's terminators (+ and \;) is that + groups as many file names that it can onto one command line. Whereas \; will execute the command for each file name.

    So, find bands/ -type f -exec grep Drums {} + will result in:

    grep Drums "bands/Dream Theater" "bands/Rush" "bands/King's X" "bands/Megadeth"
    

    and find bands/ -type f -exec grep Drums {} \; will result in:

    grep Drums "bands/Dream Theater"
    grep Drums "bands/Rush"
    grep Drums "bands/King's X"
    grep Drums "bands/Megadeth"
    

    In the case of grep this has the side effect of either printing the filename or not.

    [root@localhost bokeh]# find bands/ -type f -exec grep Drums {} \;
    Drums:Mike Mangini
    Drums: Neil Peart
    Drums:Jerry Gaskill
    Drums:Dirk Verbeuren
    
    [root@localhost bokeh]# find bands/ -type f -exec grep Drums {} +
    bands/Dream Theater:Drums:Mike Mangini
    bands/Rush:Drums: Neil Peart
    bands/King's X:Drums:Jerry Gaskill
    bands/Megadeth:Drums:Dirk Verbeuren
    

    Of course, grep's options -h and -H will control whether or not the filename is printed regardless of how grep is called.


    xargs

    xargs can also control how man files are on the command line.

    xargs by default groups all the arguments onto one line. In order to do the same thing that -exec \; does use xargs -l. Note that the -t option tells xargs to print the command before executing it.

    [root@localhost bokeh]# find ./bands -type f  | xargs -d '\n' -l -t grep Drums
    grep Drums ./bands/Dream Theater 
    Drums:Mike Mangini
    grep Drums ./bands/Rush 
    Drums: Neil Peart
    grep Drums ./bands/King's X 
    Drums:Jerry Gaskill
    grep Drums ./bands/Megadeth 
    Drums:Dirk Verbeuren
    

    See that the -l option tells xargs to execute grep for every filename.

    Versus the default (i.e. no -l option):

    [root@localhost bokeh]# find ./bands -type f  | xargs -d '\n'  -t grep Drums
    grep Drums ./bands/Dream Theater ./bands/Rush ./bands/King's X ./bands/Megadeth 
    ./bands/Dream Theater:Drums:Mike Mangini
    ./bands/Rush:Drums: Neil Peart
    ./bands/King's X:Drums:Jerry Gaskill
    ./bands/Megadeth:Drums:Dirk Verbeuren
    

    xargs has better control on how many files can be on the command line. Give the -l option the max number of files per command.

    [root@localhost bokeh]# find ./bands -type f  | xargs -d '\n'  -l2 -t grep Drums
    grep Drums ./bands/Dream Theater ./bands/Rush 
    ./bands/Dream Theater:Drums:Mike Mangini
    ./bands/Rush:Drums: Neil Peart
    grep Drums ./bands/King's X ./bands/Megadeth 
    ./bands/King's X:Drums:Jerry Gaskill
    ./bands/Megadeth:Drums:Dirk Verbeuren
    [root@localhost bokeh]# 
    

    See that grep was executed with two filenames because of -l2.

提交回复
热议问题