Is there a Linux command to copy a_b_c_d.jpg into a.jpg, b.jpg, c.jpg, and d.jpg so that each file is a copy of the original?

It should extract the name from the original name, separated by _ and ending with the first ..

closed as unclear what you're asking by andcoz, thrig, schily, Thomas, RalfFriedl Nov 23 '18 at 17:58

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 1
    What do you mean by 'split' a file? Do you want to split its name or the content? – jimmij Nov 23 '18 at 12:53
  • Welcome to Unix Stackexchange! You can take the tour first and then learn How to Ask a good question. That makes it easier for us to help you. – andcoz Nov 23 '18 at 12:55
  • I only want to split the name. Basically it should duplicate the file. – Andrei Andronache Nov 23 '18 at 13:20
  • Please past text as text. So we can all read it. – ctrl-alt-delor Nov 23 '18 at 15:51
  • Looks to me, like your trying to remove the preceding garbage infront of the original A12345_lifestyle.jpg filenames. Is this correct? – Michael Prokopec Nov 23 '18 at 16:08

Mayhap something along this line:

for FN in *.jpg; do IFS="_."; AR=($FN); for i in "${AR[@]}"; do [ ! "$i" = "jpg" ] &&  echo cp "$FN" "$i".jpg; done; done
cp a_b_c_d.jpg a.jpg
cp a_b_c_d.jpg b.jpg
cp a_b_c_d.jpg c.jpg
cp a_b_c_d.jpg d.jpg

You may want to add some more error checking, IFS safe guarding, remove the echo once you're happy with it, etc...

EDITed according to Kusalananda's comment.

for FN in *.jpg; do IFS="_"; for i in ${FN%.jpg}; do echo cp "$FN" "$i".jpg; done; done
  • Absolutely. Thanks. I Like your %.jpg approach - makes it much simpler. Should have thought of it... – RudiC Nov 23 '18 at 15:08

for name in *_*.jpg; do
    # Only care about regular files
    [ ! -f "$name" ] && continue

    # Do the following in a subshell to avoid affecting the original 
    # environment with "set -f" and setting IFS
        set -f    # Turn off filename globbing (allows filename to contain e.g. *)
        IFS='_'   # Split unquoted strings on "_" only

        # Split the filename into parts (remove trailing ".jpg" first), and then
        # iterate over the parts, creating the symbolic links
        for part in ${name%.jpg}; do
            ln -s -- "$name" "$part.jpg"

            # To do copying instead, use:
            # cp -i -- "$name" "$part.jpg"

This script would iterate over all filenames that contain at least one underscore and that ends with .jpg in the current directory. For each such name, it would split the name on underscores and create symbolic links to the original file (rather than copies) from the parts of the split up name (with the .jpg filename suffix added).

I opted for creating symbolic links rather than real copies of the original files as that would not use use up too much additional disk space. If you really wanted to do copies, then the code for that is found in a comment in the script.

Running this in a directory with the two files a_b_c_d.jpg and ABBA_hello there_misc.jpg would produce

|-- ABBA.jpg -> ABBA_hello there_misc.jpg
|-- ABBA_hello there_misc.jpg
|-- a.jpg -> a_b_c_d.jpg
|-- a_b_c_d.jpg
|-- b.jpg -> a_b_c_d.jpg
|-- c.jpg -> a_b_c_d.jpg
|-- d.jpg -> a_b_c_d.jpg
|-- hello there.jpg -> ABBA_hello there_misc.jpg
`-- misc.jpg -> ABBA_hello there_misc.jpg

A simple way is to...

for x in a b c d
  cp a_b_c_d.jpg $x.jpg

I'd hazard a guess that you want to do this more programmatically though and not have to specify the a b c d part but take it from the filename.

If this is the case then you will want to take the filename and split it twice, first based on the occurrence of . to get the file extension, and then again based on _ to get the filenames. This can be done using IFS and the looping over the array of filenames as I have done in the above original example.


Well I wrote simple script

for filename in *_*.jpg; do
   n=$(echo "$filename" | awk -F '_' '{print NF}')
   for ((i=1; i<$n;i++)) ;do
      cp "$filename" "$(echo "$filename" | awk -F '_' '{print '"\$$i"'}')".jpg
   cp "$filename" "$(echo "$filename" | awk -F '_' '{print '"\$$n"'}')"

It will search for all jpg files having atleast one_. Then using cp "$filename" "$(echo "$filename" | awk -F '_' '{print '"\$$i"'}')".jpg it will copy original file. However the last argument contains .jpg extension, so I wrote that separately outside for loop without .jpg. extension.

Not the answer you're looking for? Browse other questions tagged or ask your own question.