Replicate directory structure applying a command to each file instead of simply copying it?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP












5















Over time, I've encountered the same pattern again and again: I have some kind of directory structure:



example/
├── a
│   └── c
│   ├── d.txt (120k)
│   └── e.txt (60k)
└── b
└── f.txt (280k)


And I want to "copy" the files over to another directory, say, example_grepped, applying a command to each as if in place of cp - say, grep ERROR so that say, I end up with a folder with the same structure but with files filtered through the grep.



example_grepped/
├── a
│   └── c
│   ├── d.txt (1k)
│   └── e.txt (0b)
└── b
└── f.txt (12k)


Same pattern for converting media files (FLACs to MP3s, PNGs to JPGs), and this time when converting different schema formats as part of a build process.



Is there a generic command that I could use? Something like foobar example example_grepped --command 'grep ERROR' or foobar flacs mp3s --command 'ffmpeg -i .mp3'?



An obscure xargs flag perhaps? (a find piped through xargs would almost suffice, but most if not all commands expect the directory structure to already exist.)










share|improve this question
























  • To duplicate the directory structure you could use find with cpio. cd /path/to/example && find . -type d|cpio -pdv /path/to/example_grepped. (Omit -v if you don't want the directories to be listed while processing.) After this you can use find with xargs as proposed in the question.

    – Bodo
    Jan 23 at 12:39















5















Over time, I've encountered the same pattern again and again: I have some kind of directory structure:



example/
├── a
│   └── c
│   ├── d.txt (120k)
│   └── e.txt (60k)
└── b
└── f.txt (280k)


And I want to "copy" the files over to another directory, say, example_grepped, applying a command to each as if in place of cp - say, grep ERROR so that say, I end up with a folder with the same structure but with files filtered through the grep.



example_grepped/
├── a
│   └── c
│   ├── d.txt (1k)
│   └── e.txt (0b)
└── b
└── f.txt (12k)


Same pattern for converting media files (FLACs to MP3s, PNGs to JPGs), and this time when converting different schema formats as part of a build process.



Is there a generic command that I could use? Something like foobar example example_grepped --command 'grep ERROR' or foobar flacs mp3s --command 'ffmpeg -i .mp3'?



An obscure xargs flag perhaps? (a find piped through xargs would almost suffice, but most if not all commands expect the directory structure to already exist.)










share|improve this question
























  • To duplicate the directory structure you could use find with cpio. cd /path/to/example && find . -type d|cpio -pdv /path/to/example_grepped. (Omit -v if you don't want the directories to be listed while processing.) After this you can use find with xargs as proposed in the question.

    – Bodo
    Jan 23 at 12:39













5












5








5








Over time, I've encountered the same pattern again and again: I have some kind of directory structure:



example/
├── a
│   └── c
│   ├── d.txt (120k)
│   └── e.txt (60k)
└── b
└── f.txt (280k)


And I want to "copy" the files over to another directory, say, example_grepped, applying a command to each as if in place of cp - say, grep ERROR so that say, I end up with a folder with the same structure but with files filtered through the grep.



example_grepped/
├── a
│   └── c
│   ├── d.txt (1k)
│   └── e.txt (0b)
└── b
└── f.txt (12k)


Same pattern for converting media files (FLACs to MP3s, PNGs to JPGs), and this time when converting different schema formats as part of a build process.



Is there a generic command that I could use? Something like foobar example example_grepped --command 'grep ERROR' or foobar flacs mp3s --command 'ffmpeg -i .mp3'?



An obscure xargs flag perhaps? (a find piped through xargs would almost suffice, but most if not all commands expect the directory structure to already exist.)










share|improve this question
















Over time, I've encountered the same pattern again and again: I have some kind of directory structure:



example/
├── a
│   └── c
│   ├── d.txt (120k)
│   └── e.txt (60k)
└── b
└── f.txt (280k)


And I want to "copy" the files over to another directory, say, example_grepped, applying a command to each as if in place of cp - say, grep ERROR so that say, I end up with a folder with the same structure but with files filtered through the grep.



example_grepped/
├── a
│   └── c
│   ├── d.txt (1k)
│   └── e.txt (0b)
└── b
└── f.txt (12k)


Same pattern for converting media files (FLACs to MP3s, PNGs to JPGs), and this time when converting different schema formats as part of a build process.



Is there a generic command that I could use? Something like foobar example example_grepped --command 'grep ERROR' or foobar flacs mp3s --command 'ffmpeg -i .mp3'?



An obscure xargs flag perhaps? (a find piped through xargs would almost suffice, but most if not all commands expect the directory structure to already exist.)







xargs conversion filter






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 23 at 12:35







Tomáš M.

















asked Jan 23 at 12:29









Tomáš M.Tomáš M.

1614




1614












  • To duplicate the directory structure you could use find with cpio. cd /path/to/example && find . -type d|cpio -pdv /path/to/example_grepped. (Omit -v if you don't want the directories to be listed while processing.) After this you can use find with xargs as proposed in the question.

    – Bodo
    Jan 23 at 12:39

















  • To duplicate the directory structure you could use find with cpio. cd /path/to/example && find . -type d|cpio -pdv /path/to/example_grepped. (Omit -v if you don't want the directories to be listed while processing.) After this you can use find with xargs as proposed in the question.

    – Bodo
    Jan 23 at 12:39
















To duplicate the directory structure you could use find with cpio. cd /path/to/example && find . -type d|cpio -pdv /path/to/example_grepped. (Omit -v if you don't want the directories to be listed while processing.) After this you can use find with xargs as proposed in the question.

– Bodo
Jan 23 at 12:39





To duplicate the directory structure you could use find with cpio. cd /path/to/example && find . -type d|cpio -pdv /path/to/example_grepped. (Omit -v if you don't want the directories to be listed while processing.) After this you can use find with xargs as proposed in the question.

– Bodo
Jan 23 at 12:39










4 Answers
4






active

oldest

votes


















0














Closest answer I can find without separately recreating the directory structure is to use install:



cd example
find . -type f -exec sh -c 'grep ERROR | install -D /dev/stdin /tmp/example_grepped/' ;


Unfortunately the above can only work if your command can throw its result to STDOUT.






share|improve this answer






























    0














    Another way to approach this is to use a program that does recursive copies anyway. I checked rsync, but could not find a callback option on a quick glance. But gnu tar has an option --to-command for which you can provide a command to be run that gets the file's input into stdin. But how to get the file created then? Well, the command called finds the current file name in $TAR_FILENAME.



    Putting it all together, the basic call is



    tar cf - example | tar xf - --to-command="./script example_grepped 'grep-pattern'"


    where script could be something like



    #!/bin/bash
    mkdir -p $(dirname "$1/$TAR_FILENAME")
    grep '$2' >"$1/$TAR_FILENAME"
    exit 0


    Another way to approach this, would be to wrap the tar pipe in a script that gets the command to run on the command line. Yet the escaping for the mkdir ...dirname construct will be a bit challenging.






    share|improve this answer






























      0














      #!/bin/bash

      filter()

      local target_root="$@: -1"

      target_path=$(sed -E "s/[^/]*/$target_root/" <<< "$1")
      target_dir=$(dirname "$target_path")

      mkdir -p "$target_dir"

      if [[ -f $1 ]]; then
      # do your grep thing here
      grep burger "$1" > "$target_path"
      fi


      export -f filter
      source_root="example"
      target_root="example_grepped"

      find "$source_root/" -print0 | xargs -0 -I content bash -c "filter 'content' '$target_root'"


      This script also works with directory and file names that contain spaces.



      Run this script where the source directory ("example") is located.






      share|improve this answer
































        0














        Using GNU Parallel you can do something like this:



        cd src
        find . -type f | parallel 'mkdir -p ../dst///; dostuff --input --output ../dst/'





        share|improve this answer






















          Your Answer








          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "106"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader:
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          ,
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );













          draft saved

          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f496202%2freplicate-directory-structure-applying-a-command-to-each-file-instead-of-simply%23new-answer', 'question_page');

          );

          Post as a guest















          Required, but never shown

























          4 Answers
          4






          active

          oldest

          votes








          4 Answers
          4






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          0














          Closest answer I can find without separately recreating the directory structure is to use install:



          cd example
          find . -type f -exec sh -c 'grep ERROR | install -D /dev/stdin /tmp/example_grepped/' ;


          Unfortunately the above can only work if your command can throw its result to STDOUT.






          share|improve this answer



























            0














            Closest answer I can find without separately recreating the directory structure is to use install:



            cd example
            find . -type f -exec sh -c 'grep ERROR | install -D /dev/stdin /tmp/example_grepped/' ;


            Unfortunately the above can only work if your command can throw its result to STDOUT.






            share|improve this answer

























              0












              0








              0







              Closest answer I can find without separately recreating the directory structure is to use install:



              cd example
              find . -type f -exec sh -c 'grep ERROR | install -D /dev/stdin /tmp/example_grepped/' ;


              Unfortunately the above can only work if your command can throw its result to STDOUT.






              share|improve this answer













              Closest answer I can find without separately recreating the directory structure is to use install:



              cd example
              find . -type f -exec sh -c 'grep ERROR | install -D /dev/stdin /tmp/example_grepped/' ;


              Unfortunately the above can only work if your command can throw its result to STDOUT.







              share|improve this answer












              share|improve this answer



              share|improve this answer










              answered Jan 23 at 14:04









              GohuGohu

              8681317




              8681317























                  0














                  Another way to approach this is to use a program that does recursive copies anyway. I checked rsync, but could not find a callback option on a quick glance. But gnu tar has an option --to-command for which you can provide a command to be run that gets the file's input into stdin. But how to get the file created then? Well, the command called finds the current file name in $TAR_FILENAME.



                  Putting it all together, the basic call is



                  tar cf - example | tar xf - --to-command="./script example_grepped 'grep-pattern'"


                  where script could be something like



                  #!/bin/bash
                  mkdir -p $(dirname "$1/$TAR_FILENAME")
                  grep '$2' >"$1/$TAR_FILENAME"
                  exit 0


                  Another way to approach this, would be to wrap the tar pipe in a script that gets the command to run on the command line. Yet the escaping for the mkdir ...dirname construct will be a bit challenging.






                  share|improve this answer



























                    0














                    Another way to approach this is to use a program that does recursive copies anyway. I checked rsync, but could not find a callback option on a quick glance. But gnu tar has an option --to-command for which you can provide a command to be run that gets the file's input into stdin. But how to get the file created then? Well, the command called finds the current file name in $TAR_FILENAME.



                    Putting it all together, the basic call is



                    tar cf - example | tar xf - --to-command="./script example_grepped 'grep-pattern'"


                    where script could be something like



                    #!/bin/bash
                    mkdir -p $(dirname "$1/$TAR_FILENAME")
                    grep '$2' >"$1/$TAR_FILENAME"
                    exit 0


                    Another way to approach this, would be to wrap the tar pipe in a script that gets the command to run on the command line. Yet the escaping for the mkdir ...dirname construct will be a bit challenging.






                    share|improve this answer

























                      0












                      0








                      0







                      Another way to approach this is to use a program that does recursive copies anyway. I checked rsync, but could not find a callback option on a quick glance. But gnu tar has an option --to-command for which you can provide a command to be run that gets the file's input into stdin. But how to get the file created then? Well, the command called finds the current file name in $TAR_FILENAME.



                      Putting it all together, the basic call is



                      tar cf - example | tar xf - --to-command="./script example_grepped 'grep-pattern'"


                      where script could be something like



                      #!/bin/bash
                      mkdir -p $(dirname "$1/$TAR_FILENAME")
                      grep '$2' >"$1/$TAR_FILENAME"
                      exit 0


                      Another way to approach this, would be to wrap the tar pipe in a script that gets the command to run on the command line. Yet the escaping for the mkdir ...dirname construct will be a bit challenging.






                      share|improve this answer













                      Another way to approach this is to use a program that does recursive copies anyway. I checked rsync, but could not find a callback option on a quick glance. But gnu tar has an option --to-command for which you can provide a command to be run that gets the file's input into stdin. But how to get the file created then? Well, the command called finds the current file name in $TAR_FILENAME.



                      Putting it all together, the basic call is



                      tar cf - example | tar xf - --to-command="./script example_grepped 'grep-pattern'"


                      where script could be something like



                      #!/bin/bash
                      mkdir -p $(dirname "$1/$TAR_FILENAME")
                      grep '$2' >"$1/$TAR_FILENAME"
                      exit 0


                      Another way to approach this, would be to wrap the tar pipe in a script that gets the command to run on the command line. Yet the escaping for the mkdir ...dirname construct will be a bit challenging.







                      share|improve this answer












                      share|improve this answer



                      share|improve this answer










                      answered Jan 23 at 21:14









                      HaraldHarald

                      325112




                      325112





















                          0














                          #!/bin/bash

                          filter()

                          local target_root="$@: -1"

                          target_path=$(sed -E "s/[^/]*/$target_root/" <<< "$1")
                          target_dir=$(dirname "$target_path")

                          mkdir -p "$target_dir"

                          if [[ -f $1 ]]; then
                          # do your grep thing here
                          grep burger "$1" > "$target_path"
                          fi


                          export -f filter
                          source_root="example"
                          target_root="example_grepped"

                          find "$source_root/" -print0 | xargs -0 -I content bash -c "filter 'content' '$target_root'"


                          This script also works with directory and file names that contain spaces.



                          Run this script where the source directory ("example") is located.






                          share|improve this answer





























                            0














                            #!/bin/bash

                            filter()

                            local target_root="$@: -1"

                            target_path=$(sed -E "s/[^/]*/$target_root/" <<< "$1")
                            target_dir=$(dirname "$target_path")

                            mkdir -p "$target_dir"

                            if [[ -f $1 ]]; then
                            # do your grep thing here
                            grep burger "$1" > "$target_path"
                            fi


                            export -f filter
                            source_root="example"
                            target_root="example_grepped"

                            find "$source_root/" -print0 | xargs -0 -I content bash -c "filter 'content' '$target_root'"


                            This script also works with directory and file names that contain spaces.



                            Run this script where the source directory ("example") is located.






                            share|improve this answer



























                              0












                              0








                              0







                              #!/bin/bash

                              filter()

                              local target_root="$@: -1"

                              target_path=$(sed -E "s/[^/]*/$target_root/" <<< "$1")
                              target_dir=$(dirname "$target_path")

                              mkdir -p "$target_dir"

                              if [[ -f $1 ]]; then
                              # do your grep thing here
                              grep burger "$1" > "$target_path"
                              fi


                              export -f filter
                              source_root="example"
                              target_root="example_grepped"

                              find "$source_root/" -print0 | xargs -0 -I content bash -c "filter 'content' '$target_root'"


                              This script also works with directory and file names that contain spaces.



                              Run this script where the source directory ("example") is located.






                              share|improve this answer















                              #!/bin/bash

                              filter()

                              local target_root="$@: -1"

                              target_path=$(sed -E "s/[^/]*/$target_root/" <<< "$1")
                              target_dir=$(dirname "$target_path")

                              mkdir -p "$target_dir"

                              if [[ -f $1 ]]; then
                              # do your grep thing here
                              grep burger "$1" > "$target_path"
                              fi


                              export -f filter
                              source_root="example"
                              target_root="example_grepped"

                              find "$source_root/" -print0 | xargs -0 -I content bash -c "filter 'content' '$target_root'"


                              This script also works with directory and file names that contain spaces.



                              Run this script where the source directory ("example") is located.







                              share|improve this answer














                              share|improve this answer



                              share|improve this answer








                              edited Jan 24 at 1:02

























                              answered Jan 23 at 19:04









                              Niko GambtNiko Gambt

                              1836




                              1836





















                                  0














                                  Using GNU Parallel you can do something like this:



                                  cd src
                                  find . -type f | parallel 'mkdir -p ../dst///; dostuff --input --output ../dst/'





                                  share|improve this answer



























                                    0














                                    Using GNU Parallel you can do something like this:



                                    cd src
                                    find . -type f | parallel 'mkdir -p ../dst///; dostuff --input --output ../dst/'





                                    share|improve this answer

























                                      0












                                      0








                                      0







                                      Using GNU Parallel you can do something like this:



                                      cd src
                                      find . -type f | parallel 'mkdir -p ../dst///; dostuff --input --output ../dst/'





                                      share|improve this answer













                                      Using GNU Parallel you can do something like this:



                                      cd src
                                      find . -type f | parallel 'mkdir -p ../dst///; dostuff --input --output ../dst/'






                                      share|improve this answer












                                      share|improve this answer



                                      share|improve this answer










                                      answered Jan 30 at 19:06









                                      Ole TangeOle Tange

                                      12.4k1454105




                                      12.4k1454105



























                                          draft saved

                                          draft discarded
















































                                          Thanks for contributing an answer to Unix & Linux Stack Exchange!


                                          • Please be sure to answer the question. Provide details and share your research!

                                          But avoid


                                          • Asking for help, clarification, or responding to other answers.

                                          • Making statements based on opinion; back them up with references or personal experience.

                                          To learn more, see our tips on writing great answers.




                                          draft saved


                                          draft discarded














                                          StackExchange.ready(
                                          function ()
                                          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f496202%2freplicate-directory-structure-applying-a-command-to-each-file-instead-of-simply%23new-answer', 'question_page');

                                          );

                                          Post as a guest















                                          Required, but never shown





















































                                          Required, but never shown














                                          Required, but never shown












                                          Required, but never shown







                                          Required, but never shown

































                                          Required, but never shown














                                          Required, but never shown












                                          Required, but never shown







                                          Required, but never shown






                                          Popular posts from this blog

                                          How to check contact read email or not when send email to Individual?

                                          Displaying single band from multi-band raster using QGIS

                                          How many registers does an x86_64 CPU actually have?