LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   sed - Rule: "leftmost match, longest possible string, zero also matches."? (https://www.linuxquestions.org/questions/programming-9/sed-rule-leftmost-match-longest-possible-string-zero-also-matches-762419/)

quanta 10-16-2009 11:51 AM

mv: target `xyz' is not a directory?
 
Hi guy,

I have a set of files like that:
Code:

16 mat nai - Jimmy Nguyen [NCT 1188596568].mp3             
Nho ve em - Jimmy Nguyen [NCT 4035860162].mp3
Buon thu qua - Jimmy Nguyen [NCT 9734938264].mp3           
Nho ve em - Jimmy Nguyen [NCT 5629480849].mp3
Chiec nhan ngay xua - Jimmy Nguyen [NCT 1268575536].mp3
Nhung gi toi uoc ao - Jimmy Nguyen [NCT 2820242089].mp3
...

I want to cut the tail to make it to:
Code:

16 mat nai - Jimmy Nguyen.mp3             
Nho ve em - Jimmy Nguyen.mp3
Buon thu qua - Jimmy Nguyen.mp3           
Nho ve em - Jimmy Nguyen.mp3
Chiec nhan ngay xua - Jimmy Nguyen.mp3     
Nhung gi toi uoc ao - Jimmy Nguyen.mp3
...

I try to using a following command:
Code:

$ for f in *; do mv "$f" `echo "$f" | sed 's/ \[NCT [0-9]*\]//'`; done
or:
Code:

$ for f in *; do mv "$f" `echo "$f" | sed 's/\(.*\) \[NCT [0-9]*\].mp3$/\1.mp3/'`; done
but it didn't work:
Quote:

mv: target `Nguyen.mp3' is not a directory
I don't know why? I think it related to above rule. Can anyone help me to explain that.

Thanks in advance,

David the H. 10-16-2009 03:12 PM

The problem is most likely the spaces in the filenames. Your shell treats them as word separators, so your mv command sees the final string as being a directory name to move the rest of the entries into. Double-quoting the string that generates the new filename will probably fix it.

But my favorite way to handle spaces (other than simply not putting them in filenames to begin with) is to temporarily change the shell's internal field separator so that it doesn't treat them as places to divide words.
Code:

oldIFS=$IFS  #first save the current values (default is space, tab, newline)

IFS='
'            #changes the separator to newline only

<Run your commands here>

IFS=$oldIFS  #reset the initial values

PS: Instead of this:
Code:

`echo "$f" | sed 's/ \[NCT [0-9]*\].mp3$//'`
Try this:
Code:

"$(sed 's/ \[NCT [0-9]*\].mp3$//')" <<<"$f"
The <<< here string directs the output of the variable directly to stdin, meaning you don't have to use echo and pipes. And $() is the recommended way to nest commands. Using ` backticks is discouraged.

(assuming you're using bash, of course)


Edit again: Instead of using sed, try parameter substitution.
Code:

$ for f in *; do mv "$f" "${f% [*}.mp3"; done
${f% [*} will remove everything from " [" (space+bracket) to the end of the the variable. Then just re-add the extension and you're done.

ghostdog74 10-16-2009 08:42 PM

if you have Python and don't mind a script for that, you use the script here
Code:

# ls -1
16 mat nai - Jimmy Nguyen [NCT 1188596568].mp3
Buon thu qua - Jimmy Nguyen [NCT 9734938264].mp3
Chiec nhan ngay xua - Jimmy Nguyen [NCT 1268575536].mp3
Nho ve em - Jimmy Nguyen [NCT 4035860162].mp3
Nho ve em - Jimmy Nguyen [NCT 5629480849].mp3
Nhung gi toi uoc ao - Jimmy Nguyen [NCT 2820242089].mp3

# python filerenamer.py -p " \[.*\]" -e "" -l "*mp3"
==>>>>  [ /home/test/Chiec nhan ngay xua - Jimmy Nguyen [NCT 1268575536].mp3 ]==>[ /home/test/Chiec nhan ngay xua - Jimmy Nguyen.mp3 ]
==>>>>  [ /home/test/Nho ve em - Jimmy Nguyen [NCT 4035860162].mp3 ]==>[ /home/test/Nho ve em - Jimmy Nguyen.mp3 ]
==>>>>  [ /home/test/Nho ve em - Jimmy Nguyen [NCT 5629480849].mp3 ]==>[ /home/test/Nho ve em - Jimmy Nguyen.mp3 ]
==>>>>  [ /home/test/16 mat nai - Jimmy Nguyen [NCT 1188596568].mp3 ]==>[ /home/test/16 mat nai - Jimmy Nguyen.mp3 ]
==>>>>  [ /home/test/Buon thu qua - Jimmy Nguyen [NCT 9734938264].mp3 ]==>[ /home/test/Buon thu qua - Jimmy Nguyen.mp3 ]
==>>>>  [ /home/test/Nhung gi toi uoc ao - Jimmy Nguyen [NCT 2820242089].mp3 ]==>[ /home/test/Nhung gi toi uoc ao - Jimmy Nguyen.mp3 ]

remove the -l option to commit changes.

vonbiber 10-16-2009 09:08 PM

I just pasted your lines into a test.txt
Code:

16 mat nai - Jimmy Nguyen [NCT 1188596568].mp3
Nho ve em - Jimmy Nguyen [NCT 4035860162].mp3
Buon thu qua - Jimmy Nguyen [NCT 9734938264].mp3
Nho ve em - Jimmy Nguyen [NCT 5629480849].mp3
Chiec nhan ngay xua - Jimmy Nguyen [NCT 1268575536].mp3
Nhung gi toi uoc ao - Jimmy Nguyen [NCT 2820242089].mp3

and I ran these seders on it and got the following output
Code:

$ sed 's? \{1,\}\[?[?' test.txt |\
> sed 's?^\([^\[]*\)\[[^.]*?\1?'
16 mat nai - Jimmy Nguyen.mp3
Nho ve em - Jimmy Nguyen.mp3
Buon thu qua - Jimmy Nguyen.mp3
Nho ve em - Jimmy Nguyen.mp3
Chiec nhan ngay xua - Jimmy Nguyen.mp3
Nhung gi toi uoc ao - Jimmy Nguyen.mp3

if your files are all in the same directory you could do this
Code:

for f in *.mp3
  g="$(echo $f |sed 's? \{1,\}\[?[?' |\
    sed 's?^\([^\[]*\)\[[^.]*?\1?')"
  if [ "$f" != "$g" ]; then
    mv "$f" "$g"
  fi
done


konsolebox 10-16-2009 11:06 PM

in bash you can do something like this:
Code:

#!/bin/bash

shopt -s extglob

for F in *; do
        TITLE=${F%%?( )-*}
        ARTIST=${F##*-?( )}
        ARTIST=${ARTIST%.*([[:alpha:]])}
        ARTIST=${ARTIST/[NCT *([[:digit:]])]}
        EXT=${F##*.}

        echo "$F" "->" "$TITLE - $ARTIST.$EXT"                # optional

        mv "$F" "$TITLE - $ARTIST.$EXT"
done

i can't test the code yet. if you try it, please tell me if it works.

quanta 10-18-2009 08:37 AM

Quote:

Originally Posted by David the H. (Post 3722066)
The problem is most likely the spaces in the filenames. Your shell treats them as word separators, so your mv command sees the final string as being a directory name to move the rest of the entries into. Double-quoting the string that generates the new filename will probably fix it.

Thanks for your explain.
Quote:

Originally Posted by David the H. (Post 3722066)
But my favorite way to handle spaces (other than simply not putting them in filenames to begin with) is to temporarily change the shell's internal field separator so that it doesn't treat them as places to divide words.
Code:

oldIFS=$IFS  #first save the current values (default is space, tab, newline)

IFS='
'            #changes the separator to newline only

<Run your commands here>

IFS=$oldIFS  #reset the initial values


I know this way, but I always want to do that using one-line command.
Quote:

Originally Posted by David the H. (Post 3722066)
PS: Instead of this:
Code:

`echo "$f" | sed 's/ \[NCT [0-9]*\].mp3$//'`
Try this:
Code:

"$(sed 's/ \[NCT [0-9]*\].mp3$//')" <<<"$f"
The <<< here string directs the output of the variable directly to stdin, meaning you don't have to use echo and pipes. And $() is the recommended way to nest commands. Using ` backticks is discouraged.

(assuming you're using bash, of course)

Oh, now I know about here string, but can you show me a full command to do this?
Quote:

Originally Posted by David the H. (Post 3722066)
Edit again: Instead of using sed, try parameter substitution.
Code:

$ for f in *; do mv "$f" "${f% [*}.mp3"; done
${f% [*} will remove everything from " [" (space+bracket) to the end of the the variable. Then just re-add the extension and you're done.

Thanks again. I think I need to re-read the ABS Guide.

@ghostdog74: I am learning Python too, I will consider it later.

@vonbiber, konsolebox: thanks for your help. But as I said above, I want to do that using one-line command.

****************************************************************

Another question from a my friend, seem to more relate to title of this topic:

Output from jobs -l:
Code:

[1]+  7989 Running                okular sed.stream.editor.cheat.sheet.pdf &
He want to get PID (7989). He know a better command with awk:
Code:

$ jobs -l | awk '{ print $2 }'
but he tried another command with sed:
Code:

jobs -l | sed -n 's/^\[[0-9]*\].*\([0-9]*\).*$/\1/p
and it didn't work, it return a blank line. Can anyone explain that for us?


All times are GMT -5. The time now is 05:27 AM.