looping through `ls` results in bash shell script












82















Does any one have a template shell script for doing something with ls for a list of directory names and looping through each one and doing something?



I'm planning to do ls -1d */ to get the list of directory names.










share|improve this question





























    82















    Does any one have a template shell script for doing something with ls for a list of directory names and looping through each one and doing something?



    I'm planning to do ls -1d */ to get the list of directory names.










    share|improve this question



























      82












      82








      82


      20






      Does any one have a template shell script for doing something with ls for a list of directory names and looping through each one and doing something?



      I'm planning to do ls -1d */ to get the list of directory names.










      share|improve this question
















      Does any one have a template shell script for doing something with ls for a list of directory names and looping through each one and doing something?



      I'm planning to do ls -1d */ to get the list of directory names.







      bash shell shell-script






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Sep 6 '17 at 13:30







      Daniel A. White

















      asked Aug 28 '09 at 17:03









      Daniel A. WhiteDaniel A. White

      2,67311929




      2,67311929






















          7 Answers
          7






          active

          oldest

          votes


















          86














          Edited not to use ls where a glob would do, as @shawn-j-goff and others suggested.



          Just use a for..do..done loop:



          for f in *; do
          echo "File -> $f"
          done


          You can replace the * with *.txt or any other glob that returns a list (of files, directories, or anything for that matter), a command that generates a list, e.g., $(cat filelist.txt), or actually replace it with a list.



          Within the do loop, you just refer to the loop variable with the dollar sign prefix (so $f in the above example). You can echo it or do anything else to it you want.



          For example, to rename all the .xml files in the current directory to .txt:



          for x in *.xml; do 
          t=$(echo $x | sed 's/.xml$/.txt/');
          mv $x $t && echo "moved $x -> $t"
          done


          Or even better, if you are using Bash you can use Bash parameter expansions rather than spawning a subshell:



          for x in *.xml; do 
          t=${x%.xml}.txt
          mv $x $t && echo "moved $x -> $t"
          done





          share|improve this answer





















          • 19





            What if the filename has a space in it?

            – Daniel A. White
            Aug 28 '09 at 17:17






          • 4





            Unfortunately, as Daniel said, the code in this answer will break if any of the files or folders contains a space or newline in their name. It shows a very common misuse of for loops, and a typical pitfall of trying to parse the output of ls. @DanielA.White, you might consider unaccepting this answer if it wasn't helpful (or is potentially misleading), since like you said, you're acting on directories. Shawn J. Goff's answer should provide a more robust and working solution to your issue.

            – slhck
            Jan 23 '13 at 17:48






          • 4





            -∞ You shouldn't parse ls output, you shouldn't read output in a for loop, you should use $() instead of `` and Use More Quotes™. I'm sure @CoverosGene meant well, but this is just terrible.

            – l0b0
            Apr 17 '14 at 19:31








          • 1





            A better alternative if you really want to use ls is ls -1 | while read line; do stuff; done. At least that one won't break for whitespaces.

            – Emmanuel Joubaud
            Aug 1 '14 at 8:47






          • 1





            with mv -v you don't need echo "moved $x -> $t"

            – DmitrySandalov
            Jan 15 '16 at 12:59



















          63














          Using the output of ls to get filenames is a bad idea. It can lead to malfunctioning and even dangerous scripts. This is because a filename can contain any character except / and the nullcharacter, and ls does not use either of those characters as delimiters, so if a filename has a space or a newline, you will get unexpected results.



          There are two very good ways of iterating over files. Here, I've used simply echo to demonstrate doing something with the filename; you can use anything, though.



          The first is to use the shell's native globbing features.



          for dir in */; do
          echo "$dir"
          done


          The shell expands */ into separate arguments that the for loop reads; even if there is a space, newline, or any other strange character in the filename, for will see each complete name as an atomic unit; it's not parsing the list in any way.



          If you want to go recursively into subdirectories, then this won't do unless your shell has some extended globbing features (such as bash's globstar. If your shell doesn't have these features, or if you want to ensure that your script will work on a variety of systems, then the next option is to use find.



          find . -type d -exec echo '{}' ;


          Here, the find command will call echo and pass it an argument of the filename. It does this once for each file it finds. As with the previous example, there is no parsing of a list of filenames; instead, a fileneame is sent completely as an argument.



          The syntax of the -exec argument looks a little funny. find takes the first argument after -exec and treats that as the program to run, and every subsequent argument, it takes as an argument to pass to that program. There are two special arguments that -exec needs to see. The first one is {}; this argument gets replaced with a filename that the previous parts of find generates. The second one is ;, which lets find know this is the end of the list of arguments to pass to the program; find needs this because you can continue with more arguments that are intended for find and not intended for the exec'd program. The reason for the is that the shell also treats ; specially - it represents the end of a command, so we need to escape it so that the shell gives it as an argument to find rather than consuming it for itself; another way of getting the shell to not treat it specially is to put it in in quotes: ';' works just as well as ; for this purpose.






          share|improve this answer





















          • 2





            +1 this is definitely the way to go when you need to generate a file list and use it in a command. find -exec is limited by only being able to run a single commands. With the loop, you can pipe to your heart's content.

            – MaQleod
            May 10 '12 at 22:45











          • +1. I wish more people would use find. There is magic that can be done, and even the perceived limitations of -exec can be worked around. The -print0 option is also valuable for use with xargs.

            – ghoti
            Oct 17 '14 at 22:24



















          9














          For files with spaces in you will have to make sure to quote the variable like:



           for i in $(ls); do echo "$i"; done; 


          or, you can change the input field separator (IFS) environment variable:



           IFS=$'n';for file in $(ls); do echo $i; done


          Finally, depending on what you're doing, you may not even need the ls:



           for i in *; do echo "$i"; done;





          share|improve this answer
























          • nice use of a subshell in the first example

            – Jeremy L
            Aug 28 '09 at 18:46











          • Why does IFS need a $ after the assignment and before the new character?

            – Andy Ibanez
            Nov 29 '12 at 18:57






          • 3





            Filenames can also contain newlines. Breaking on n is insufficient. It's never a good idea to recommend parsing the output of ls.

            – ghoti
            Oct 17 '14 at 22:18





















          5














          If you have GNU Parallel http://www.gnu.org/software/parallel/ installed you can do this:



          ls | parallel echo {} is in this dir


          To rename all .txt to .xml:



          ls *.txt | parallel mv {} {.}.xml


          Watch the intro video for GNU Parallel to learn more:
          http://www.youtube.com/watch?v=OpaiGYxkSuQ






          share|improve this answer

































            4














            Just to add to CoverosGene's answer, here is a way to list just the directory names:



            for f in */; do
            echo "Directory -> $f"
            done





            share|improve this answer































              1














              Why not set IFS to a newline, then capture the output of ls in an array? Setting IFS to newline should resolve issues with funny characters in file names; using ls can be nice because it has a built-in sort facility.



              (In testing I was having trouble setting IFS to n but setting it to newline backspace works, as suggested elsewhere here):



              E.g. (assuming desired ls search pattern passed in $1):



              SAVEIFS=$IFS
              IFS=$(echo -en "nb")

              FILES=($(/bin/ls "$1"))

              for AFILE in ${FILES[@]}
              do
              ... do something with a file ...
              done

              IFS=$SAVEIFS


              This is especially handy on OS X, e.g., to capture a list of files sorted by creation date (oldest to newest), the ls command is ls -t -U -r.






              share|improve this answer





















              • 2





                Filenames can also contain newlines, and they often do when users are permitted to name their own files. Breaking on n is insufficient. The only valid solutions use a for loop with pathname expansion, or find.

                – ghoti
                Oct 17 '14 at 22:21











              • The only reliable way to transfer a list of file names is to separate them with a NUL character, as this is the only one definitely not contained in a file path.

                – glglgl
                Jan 2 '15 at 9:23





















              -3














              This is how I do it, but there are probably more efficient ways.



              ls > filelist.txt

              while read filename; do echo filename: "$filename"; done < filelist.txt





              share|improve this answer



















              • 6





                Stick to pipes in place of the file: >ls | while read i; do echo filename: $i; done

                – Jeremy L
                Aug 28 '09 at 18:46











              • Cool. I should say that you can also use $EDITOR filelist.txt in between the two commands. Lots of stuff you can do in an editor that is easier than on the command line. Not relevant to this question, though.

                – TREE
                Aug 31 '09 at 18:45











              • Your solution doesn't at all address the problem with file names containing newlines and other fancy stuff.

                – glglgl
                Jan 2 '15 at 9:24











              Your Answer








              StackExchange.ready(function() {
              var channelOptions = {
              tags: "".split(" "),
              id: "3"
              };
              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: true,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: 10,
              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%2fsuperuser.com%2fquestions%2f31464%2flooping-through-ls-results-in-bash-shell-script%23new-answer', 'question_page');
              }
              );

              Post as a guest















              Required, but never shown

























              7 Answers
              7






              active

              oldest

              votes








              7 Answers
              7






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes









              86














              Edited not to use ls where a glob would do, as @shawn-j-goff and others suggested.



              Just use a for..do..done loop:



              for f in *; do
              echo "File -> $f"
              done


              You can replace the * with *.txt or any other glob that returns a list (of files, directories, or anything for that matter), a command that generates a list, e.g., $(cat filelist.txt), or actually replace it with a list.



              Within the do loop, you just refer to the loop variable with the dollar sign prefix (so $f in the above example). You can echo it or do anything else to it you want.



              For example, to rename all the .xml files in the current directory to .txt:



              for x in *.xml; do 
              t=$(echo $x | sed 's/.xml$/.txt/');
              mv $x $t && echo "moved $x -> $t"
              done


              Or even better, if you are using Bash you can use Bash parameter expansions rather than spawning a subshell:



              for x in *.xml; do 
              t=${x%.xml}.txt
              mv $x $t && echo "moved $x -> $t"
              done





              share|improve this answer





















              • 19





                What if the filename has a space in it?

                – Daniel A. White
                Aug 28 '09 at 17:17






              • 4





                Unfortunately, as Daniel said, the code in this answer will break if any of the files or folders contains a space or newline in their name. It shows a very common misuse of for loops, and a typical pitfall of trying to parse the output of ls. @DanielA.White, you might consider unaccepting this answer if it wasn't helpful (or is potentially misleading), since like you said, you're acting on directories. Shawn J. Goff's answer should provide a more robust and working solution to your issue.

                – slhck
                Jan 23 '13 at 17:48






              • 4





                -∞ You shouldn't parse ls output, you shouldn't read output in a for loop, you should use $() instead of `` and Use More Quotes™. I'm sure @CoverosGene meant well, but this is just terrible.

                – l0b0
                Apr 17 '14 at 19:31








              • 1





                A better alternative if you really want to use ls is ls -1 | while read line; do stuff; done. At least that one won't break for whitespaces.

                – Emmanuel Joubaud
                Aug 1 '14 at 8:47






              • 1





                with mv -v you don't need echo "moved $x -> $t"

                – DmitrySandalov
                Jan 15 '16 at 12:59
















              86














              Edited not to use ls where a glob would do, as @shawn-j-goff and others suggested.



              Just use a for..do..done loop:



              for f in *; do
              echo "File -> $f"
              done


              You can replace the * with *.txt or any other glob that returns a list (of files, directories, or anything for that matter), a command that generates a list, e.g., $(cat filelist.txt), or actually replace it with a list.



              Within the do loop, you just refer to the loop variable with the dollar sign prefix (so $f in the above example). You can echo it or do anything else to it you want.



              For example, to rename all the .xml files in the current directory to .txt:



              for x in *.xml; do 
              t=$(echo $x | sed 's/.xml$/.txt/');
              mv $x $t && echo "moved $x -> $t"
              done


              Or even better, if you are using Bash you can use Bash parameter expansions rather than spawning a subshell:



              for x in *.xml; do 
              t=${x%.xml}.txt
              mv $x $t && echo "moved $x -> $t"
              done





              share|improve this answer





















              • 19





                What if the filename has a space in it?

                – Daniel A. White
                Aug 28 '09 at 17:17






              • 4





                Unfortunately, as Daniel said, the code in this answer will break if any of the files or folders contains a space or newline in their name. It shows a very common misuse of for loops, and a typical pitfall of trying to parse the output of ls. @DanielA.White, you might consider unaccepting this answer if it wasn't helpful (or is potentially misleading), since like you said, you're acting on directories. Shawn J. Goff's answer should provide a more robust and working solution to your issue.

                – slhck
                Jan 23 '13 at 17:48






              • 4





                -∞ You shouldn't parse ls output, you shouldn't read output in a for loop, you should use $() instead of `` and Use More Quotes™. I'm sure @CoverosGene meant well, but this is just terrible.

                – l0b0
                Apr 17 '14 at 19:31








              • 1





                A better alternative if you really want to use ls is ls -1 | while read line; do stuff; done. At least that one won't break for whitespaces.

                – Emmanuel Joubaud
                Aug 1 '14 at 8:47






              • 1





                with mv -v you don't need echo "moved $x -> $t"

                – DmitrySandalov
                Jan 15 '16 at 12:59














              86












              86








              86







              Edited not to use ls where a glob would do, as @shawn-j-goff and others suggested.



              Just use a for..do..done loop:



              for f in *; do
              echo "File -> $f"
              done


              You can replace the * with *.txt or any other glob that returns a list (of files, directories, or anything for that matter), a command that generates a list, e.g., $(cat filelist.txt), or actually replace it with a list.



              Within the do loop, you just refer to the loop variable with the dollar sign prefix (so $f in the above example). You can echo it or do anything else to it you want.



              For example, to rename all the .xml files in the current directory to .txt:



              for x in *.xml; do 
              t=$(echo $x | sed 's/.xml$/.txt/');
              mv $x $t && echo "moved $x -> $t"
              done


              Or even better, if you are using Bash you can use Bash parameter expansions rather than spawning a subshell:



              for x in *.xml; do 
              t=${x%.xml}.txt
              mv $x $t && echo "moved $x -> $t"
              done





              share|improve this answer















              Edited not to use ls where a glob would do, as @shawn-j-goff and others suggested.



              Just use a for..do..done loop:



              for f in *; do
              echo "File -> $f"
              done


              You can replace the * with *.txt or any other glob that returns a list (of files, directories, or anything for that matter), a command that generates a list, e.g., $(cat filelist.txt), or actually replace it with a list.



              Within the do loop, you just refer to the loop variable with the dollar sign prefix (so $f in the above example). You can echo it or do anything else to it you want.



              For example, to rename all the .xml files in the current directory to .txt:



              for x in *.xml; do 
              t=$(echo $x | sed 's/.xml$/.txt/');
              mv $x $t && echo "moved $x -> $t"
              done


              Or even better, if you are using Bash you can use Bash parameter expansions rather than spawning a subshell:



              for x in *.xml; do 
              t=${x%.xml}.txt
              mv $x $t && echo "moved $x -> $t"
              done






              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited May 8 '15 at 0:00









              Michael Frank

              6,38713044




              6,38713044










              answered Aug 28 '09 at 17:09









              CoverosGeneCoverosGene

              1,138106




              1,138106








              • 19





                What if the filename has a space in it?

                – Daniel A. White
                Aug 28 '09 at 17:17






              • 4





                Unfortunately, as Daniel said, the code in this answer will break if any of the files or folders contains a space or newline in their name. It shows a very common misuse of for loops, and a typical pitfall of trying to parse the output of ls. @DanielA.White, you might consider unaccepting this answer if it wasn't helpful (or is potentially misleading), since like you said, you're acting on directories. Shawn J. Goff's answer should provide a more robust and working solution to your issue.

                – slhck
                Jan 23 '13 at 17:48






              • 4





                -∞ You shouldn't parse ls output, you shouldn't read output in a for loop, you should use $() instead of `` and Use More Quotes™. I'm sure @CoverosGene meant well, but this is just terrible.

                – l0b0
                Apr 17 '14 at 19:31








              • 1





                A better alternative if you really want to use ls is ls -1 | while read line; do stuff; done. At least that one won't break for whitespaces.

                – Emmanuel Joubaud
                Aug 1 '14 at 8:47






              • 1





                with mv -v you don't need echo "moved $x -> $t"

                – DmitrySandalov
                Jan 15 '16 at 12:59














              • 19





                What if the filename has a space in it?

                – Daniel A. White
                Aug 28 '09 at 17:17






              • 4





                Unfortunately, as Daniel said, the code in this answer will break if any of the files or folders contains a space or newline in their name. It shows a very common misuse of for loops, and a typical pitfall of trying to parse the output of ls. @DanielA.White, you might consider unaccepting this answer if it wasn't helpful (or is potentially misleading), since like you said, you're acting on directories. Shawn J. Goff's answer should provide a more robust and working solution to your issue.

                – slhck
                Jan 23 '13 at 17:48






              • 4





                -∞ You shouldn't parse ls output, you shouldn't read output in a for loop, you should use $() instead of `` and Use More Quotes™. I'm sure @CoverosGene meant well, but this is just terrible.

                – l0b0
                Apr 17 '14 at 19:31








              • 1





                A better alternative if you really want to use ls is ls -1 | while read line; do stuff; done. At least that one won't break for whitespaces.

                – Emmanuel Joubaud
                Aug 1 '14 at 8:47






              • 1





                with mv -v you don't need echo "moved $x -> $t"

                – DmitrySandalov
                Jan 15 '16 at 12:59








              19




              19





              What if the filename has a space in it?

              – Daniel A. White
              Aug 28 '09 at 17:17





              What if the filename has a space in it?

              – Daniel A. White
              Aug 28 '09 at 17:17




              4




              4





              Unfortunately, as Daniel said, the code in this answer will break if any of the files or folders contains a space or newline in their name. It shows a very common misuse of for loops, and a typical pitfall of trying to parse the output of ls. @DanielA.White, you might consider unaccepting this answer if it wasn't helpful (or is potentially misleading), since like you said, you're acting on directories. Shawn J. Goff's answer should provide a more robust and working solution to your issue.

              – slhck
              Jan 23 '13 at 17:48





              Unfortunately, as Daniel said, the code in this answer will break if any of the files or folders contains a space or newline in their name. It shows a very common misuse of for loops, and a typical pitfall of trying to parse the output of ls. @DanielA.White, you might consider unaccepting this answer if it wasn't helpful (or is potentially misleading), since like you said, you're acting on directories. Shawn J. Goff's answer should provide a more robust and working solution to your issue.

              – slhck
              Jan 23 '13 at 17:48




              4




              4





              -∞ You shouldn't parse ls output, you shouldn't read output in a for loop, you should use $() instead of `` and Use More Quotes™. I'm sure @CoverosGene meant well, but this is just terrible.

              – l0b0
              Apr 17 '14 at 19:31







              -∞ You shouldn't parse ls output, you shouldn't read output in a for loop, you should use $() instead of `` and Use More Quotes™. I'm sure @CoverosGene meant well, but this is just terrible.

              – l0b0
              Apr 17 '14 at 19:31






              1




              1





              A better alternative if you really want to use ls is ls -1 | while read line; do stuff; done. At least that one won't break for whitespaces.

              – Emmanuel Joubaud
              Aug 1 '14 at 8:47





              A better alternative if you really want to use ls is ls -1 | while read line; do stuff; done. At least that one won't break for whitespaces.

              – Emmanuel Joubaud
              Aug 1 '14 at 8:47




              1




              1





              with mv -v you don't need echo "moved $x -> $t"

              – DmitrySandalov
              Jan 15 '16 at 12:59





              with mv -v you don't need echo "moved $x -> $t"

              – DmitrySandalov
              Jan 15 '16 at 12:59













              63














              Using the output of ls to get filenames is a bad idea. It can lead to malfunctioning and even dangerous scripts. This is because a filename can contain any character except / and the nullcharacter, and ls does not use either of those characters as delimiters, so if a filename has a space or a newline, you will get unexpected results.



              There are two very good ways of iterating over files. Here, I've used simply echo to demonstrate doing something with the filename; you can use anything, though.



              The first is to use the shell's native globbing features.



              for dir in */; do
              echo "$dir"
              done


              The shell expands */ into separate arguments that the for loop reads; even if there is a space, newline, or any other strange character in the filename, for will see each complete name as an atomic unit; it's not parsing the list in any way.



              If you want to go recursively into subdirectories, then this won't do unless your shell has some extended globbing features (such as bash's globstar. If your shell doesn't have these features, or if you want to ensure that your script will work on a variety of systems, then the next option is to use find.



              find . -type d -exec echo '{}' ;


              Here, the find command will call echo and pass it an argument of the filename. It does this once for each file it finds. As with the previous example, there is no parsing of a list of filenames; instead, a fileneame is sent completely as an argument.



              The syntax of the -exec argument looks a little funny. find takes the first argument after -exec and treats that as the program to run, and every subsequent argument, it takes as an argument to pass to that program. There are two special arguments that -exec needs to see. The first one is {}; this argument gets replaced with a filename that the previous parts of find generates. The second one is ;, which lets find know this is the end of the list of arguments to pass to the program; find needs this because you can continue with more arguments that are intended for find and not intended for the exec'd program. The reason for the is that the shell also treats ; specially - it represents the end of a command, so we need to escape it so that the shell gives it as an argument to find rather than consuming it for itself; another way of getting the shell to not treat it specially is to put it in in quotes: ';' works just as well as ; for this purpose.






              share|improve this answer





















              • 2





                +1 this is definitely the way to go when you need to generate a file list and use it in a command. find -exec is limited by only being able to run a single commands. With the loop, you can pipe to your heart's content.

                – MaQleod
                May 10 '12 at 22:45











              • +1. I wish more people would use find. There is magic that can be done, and even the perceived limitations of -exec can be worked around. The -print0 option is also valuable for use with xargs.

                – ghoti
                Oct 17 '14 at 22:24
















              63














              Using the output of ls to get filenames is a bad idea. It can lead to malfunctioning and even dangerous scripts. This is because a filename can contain any character except / and the nullcharacter, and ls does not use either of those characters as delimiters, so if a filename has a space or a newline, you will get unexpected results.



              There are two very good ways of iterating over files. Here, I've used simply echo to demonstrate doing something with the filename; you can use anything, though.



              The first is to use the shell's native globbing features.



              for dir in */; do
              echo "$dir"
              done


              The shell expands */ into separate arguments that the for loop reads; even if there is a space, newline, or any other strange character in the filename, for will see each complete name as an atomic unit; it's not parsing the list in any way.



              If you want to go recursively into subdirectories, then this won't do unless your shell has some extended globbing features (such as bash's globstar. If your shell doesn't have these features, or if you want to ensure that your script will work on a variety of systems, then the next option is to use find.



              find . -type d -exec echo '{}' ;


              Here, the find command will call echo and pass it an argument of the filename. It does this once for each file it finds. As with the previous example, there is no parsing of a list of filenames; instead, a fileneame is sent completely as an argument.



              The syntax of the -exec argument looks a little funny. find takes the first argument after -exec and treats that as the program to run, and every subsequent argument, it takes as an argument to pass to that program. There are two special arguments that -exec needs to see. The first one is {}; this argument gets replaced with a filename that the previous parts of find generates. The second one is ;, which lets find know this is the end of the list of arguments to pass to the program; find needs this because you can continue with more arguments that are intended for find and not intended for the exec'd program. The reason for the is that the shell also treats ; specially - it represents the end of a command, so we need to escape it so that the shell gives it as an argument to find rather than consuming it for itself; another way of getting the shell to not treat it specially is to put it in in quotes: ';' works just as well as ; for this purpose.






              share|improve this answer





















              • 2





                +1 this is definitely the way to go when you need to generate a file list and use it in a command. find -exec is limited by only being able to run a single commands. With the loop, you can pipe to your heart's content.

                – MaQleod
                May 10 '12 at 22:45











              • +1. I wish more people would use find. There is magic that can be done, and even the perceived limitations of -exec can be worked around. The -print0 option is also valuable for use with xargs.

                – ghoti
                Oct 17 '14 at 22:24














              63












              63








              63







              Using the output of ls to get filenames is a bad idea. It can lead to malfunctioning and even dangerous scripts. This is because a filename can contain any character except / and the nullcharacter, and ls does not use either of those characters as delimiters, so if a filename has a space or a newline, you will get unexpected results.



              There are two very good ways of iterating over files. Here, I've used simply echo to demonstrate doing something with the filename; you can use anything, though.



              The first is to use the shell's native globbing features.



              for dir in */; do
              echo "$dir"
              done


              The shell expands */ into separate arguments that the for loop reads; even if there is a space, newline, or any other strange character in the filename, for will see each complete name as an atomic unit; it's not parsing the list in any way.



              If you want to go recursively into subdirectories, then this won't do unless your shell has some extended globbing features (such as bash's globstar. If your shell doesn't have these features, or if you want to ensure that your script will work on a variety of systems, then the next option is to use find.



              find . -type d -exec echo '{}' ;


              Here, the find command will call echo and pass it an argument of the filename. It does this once for each file it finds. As with the previous example, there is no parsing of a list of filenames; instead, a fileneame is sent completely as an argument.



              The syntax of the -exec argument looks a little funny. find takes the first argument after -exec and treats that as the program to run, and every subsequent argument, it takes as an argument to pass to that program. There are two special arguments that -exec needs to see. The first one is {}; this argument gets replaced with a filename that the previous parts of find generates. The second one is ;, which lets find know this is the end of the list of arguments to pass to the program; find needs this because you can continue with more arguments that are intended for find and not intended for the exec'd program. The reason for the is that the shell also treats ; specially - it represents the end of a command, so we need to escape it so that the shell gives it as an argument to find rather than consuming it for itself; another way of getting the shell to not treat it specially is to put it in in quotes: ';' works just as well as ; for this purpose.






              share|improve this answer















              Using the output of ls to get filenames is a bad idea. It can lead to malfunctioning and even dangerous scripts. This is because a filename can contain any character except / and the nullcharacter, and ls does not use either of those characters as delimiters, so if a filename has a space or a newline, you will get unexpected results.



              There are two very good ways of iterating over files. Here, I've used simply echo to demonstrate doing something with the filename; you can use anything, though.



              The first is to use the shell's native globbing features.



              for dir in */; do
              echo "$dir"
              done


              The shell expands */ into separate arguments that the for loop reads; even if there is a space, newline, or any other strange character in the filename, for will see each complete name as an atomic unit; it's not parsing the list in any way.



              If you want to go recursively into subdirectories, then this won't do unless your shell has some extended globbing features (such as bash's globstar. If your shell doesn't have these features, or if you want to ensure that your script will work on a variety of systems, then the next option is to use find.



              find . -type d -exec echo '{}' ;


              Here, the find command will call echo and pass it an argument of the filename. It does this once for each file it finds. As with the previous example, there is no parsing of a list of filenames; instead, a fileneame is sent completely as an argument.



              The syntax of the -exec argument looks a little funny. find takes the first argument after -exec and treats that as the program to run, and every subsequent argument, it takes as an argument to pass to that program. There are two special arguments that -exec needs to see. The first one is {}; this argument gets replaced with a filename that the previous parts of find generates. The second one is ;, which lets find know this is the end of the list of arguments to pass to the program; find needs this because you can continue with more arguments that are intended for find and not intended for the exec'd program. The reason for the is that the shell also treats ; specially - it represents the end of a command, so we need to escape it so that the shell gives it as an argument to find rather than consuming it for itself; another way of getting the shell to not treat it specially is to put it in in quotes: ';' works just as well as ; for this purpose.







              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited Jul 23 '14 at 13:00

























              answered Apr 29 '12 at 22:16









              Shawn J. GoffShawn J. Goff

              7881716




              7881716








              • 2





                +1 this is definitely the way to go when you need to generate a file list and use it in a command. find -exec is limited by only being able to run a single commands. With the loop, you can pipe to your heart's content.

                – MaQleod
                May 10 '12 at 22:45











              • +1. I wish more people would use find. There is magic that can be done, and even the perceived limitations of -exec can be worked around. The -print0 option is also valuable for use with xargs.

                – ghoti
                Oct 17 '14 at 22:24














              • 2





                +1 this is definitely the way to go when you need to generate a file list and use it in a command. find -exec is limited by only being able to run a single commands. With the loop, you can pipe to your heart's content.

                – MaQleod
                May 10 '12 at 22:45











              • +1. I wish more people would use find. There is magic that can be done, and even the perceived limitations of -exec can be worked around. The -print0 option is also valuable for use with xargs.

                – ghoti
                Oct 17 '14 at 22:24








              2




              2





              +1 this is definitely the way to go when you need to generate a file list and use it in a command. find -exec is limited by only being able to run a single commands. With the loop, you can pipe to your heart's content.

              – MaQleod
              May 10 '12 at 22:45





              +1 this is definitely the way to go when you need to generate a file list and use it in a command. find -exec is limited by only being able to run a single commands. With the loop, you can pipe to your heart's content.

              – MaQleod
              May 10 '12 at 22:45













              +1. I wish more people would use find. There is magic that can be done, and even the perceived limitations of -exec can be worked around. The -print0 option is also valuable for use with xargs.

              – ghoti
              Oct 17 '14 at 22:24





              +1. I wish more people would use find. There is magic that can be done, and even the perceived limitations of -exec can be worked around. The -print0 option is also valuable for use with xargs.

              – ghoti
              Oct 17 '14 at 22:24











              9














              For files with spaces in you will have to make sure to quote the variable like:



               for i in $(ls); do echo "$i"; done; 


              or, you can change the input field separator (IFS) environment variable:



               IFS=$'n';for file in $(ls); do echo $i; done


              Finally, depending on what you're doing, you may not even need the ls:



               for i in *; do echo "$i"; done;





              share|improve this answer
























              • nice use of a subshell in the first example

                – Jeremy L
                Aug 28 '09 at 18:46











              • Why does IFS need a $ after the assignment and before the new character?

                – Andy Ibanez
                Nov 29 '12 at 18:57






              • 3





                Filenames can also contain newlines. Breaking on n is insufficient. It's never a good idea to recommend parsing the output of ls.

                – ghoti
                Oct 17 '14 at 22:18


















              9














              For files with spaces in you will have to make sure to quote the variable like:



               for i in $(ls); do echo "$i"; done; 


              or, you can change the input field separator (IFS) environment variable:



               IFS=$'n';for file in $(ls); do echo $i; done


              Finally, depending on what you're doing, you may not even need the ls:



               for i in *; do echo "$i"; done;





              share|improve this answer
























              • nice use of a subshell in the first example

                – Jeremy L
                Aug 28 '09 at 18:46











              • Why does IFS need a $ after the assignment and before the new character?

                – Andy Ibanez
                Nov 29 '12 at 18:57






              • 3





                Filenames can also contain newlines. Breaking on n is insufficient. It's never a good idea to recommend parsing the output of ls.

                – ghoti
                Oct 17 '14 at 22:18
















              9












              9








              9







              For files with spaces in you will have to make sure to quote the variable like:



               for i in $(ls); do echo "$i"; done; 


              or, you can change the input field separator (IFS) environment variable:



               IFS=$'n';for file in $(ls); do echo $i; done


              Finally, depending on what you're doing, you may not even need the ls:



               for i in *; do echo "$i"; done;





              share|improve this answer













              For files with spaces in you will have to make sure to quote the variable like:



               for i in $(ls); do echo "$i"; done; 


              or, you can change the input field separator (IFS) environment variable:



               IFS=$'n';for file in $(ls); do echo $i; done


              Finally, depending on what you're doing, you may not even need the ls:



               for i in *; do echo "$i"; done;






              share|improve this answer












              share|improve this answer



              share|improve this answer










              answered Aug 28 '09 at 17:26









              johnjohn

              45726




              45726













              • nice use of a subshell in the first example

                – Jeremy L
                Aug 28 '09 at 18:46











              • Why does IFS need a $ after the assignment and before the new character?

                – Andy Ibanez
                Nov 29 '12 at 18:57






              • 3





                Filenames can also contain newlines. Breaking on n is insufficient. It's never a good idea to recommend parsing the output of ls.

                – ghoti
                Oct 17 '14 at 22:18





















              • nice use of a subshell in the first example

                – Jeremy L
                Aug 28 '09 at 18:46











              • Why does IFS need a $ after the assignment and before the new character?

                – Andy Ibanez
                Nov 29 '12 at 18:57






              • 3





                Filenames can also contain newlines. Breaking on n is insufficient. It's never a good idea to recommend parsing the output of ls.

                – ghoti
                Oct 17 '14 at 22:18



















              nice use of a subshell in the first example

              – Jeremy L
              Aug 28 '09 at 18:46





              nice use of a subshell in the first example

              – Jeremy L
              Aug 28 '09 at 18:46













              Why does IFS need a $ after the assignment and before the new character?

              – Andy Ibanez
              Nov 29 '12 at 18:57





              Why does IFS need a $ after the assignment and before the new character?

              – Andy Ibanez
              Nov 29 '12 at 18:57




              3




              3





              Filenames can also contain newlines. Breaking on n is insufficient. It's never a good idea to recommend parsing the output of ls.

              – ghoti
              Oct 17 '14 at 22:18







              Filenames can also contain newlines. Breaking on n is insufficient. It's never a good idea to recommend parsing the output of ls.

              – ghoti
              Oct 17 '14 at 22:18













              5














              If you have GNU Parallel http://www.gnu.org/software/parallel/ installed you can do this:



              ls | parallel echo {} is in this dir


              To rename all .txt to .xml:



              ls *.txt | parallel mv {} {.}.xml


              Watch the intro video for GNU Parallel to learn more:
              http://www.youtube.com/watch?v=OpaiGYxkSuQ






              share|improve this answer






























                5














                If you have GNU Parallel http://www.gnu.org/software/parallel/ installed you can do this:



                ls | parallel echo {} is in this dir


                To rename all .txt to .xml:



                ls *.txt | parallel mv {} {.}.xml


                Watch the intro video for GNU Parallel to learn more:
                http://www.youtube.com/watch?v=OpaiGYxkSuQ






                share|improve this answer




























                  5












                  5








                  5







                  If you have GNU Parallel http://www.gnu.org/software/parallel/ installed you can do this:



                  ls | parallel echo {} is in this dir


                  To rename all .txt to .xml:



                  ls *.txt | parallel mv {} {.}.xml


                  Watch the intro video for GNU Parallel to learn more:
                  http://www.youtube.com/watch?v=OpaiGYxkSuQ






                  share|improve this answer















                  If you have GNU Parallel http://www.gnu.org/software/parallel/ installed you can do this:



                  ls | parallel echo {} is in this dir


                  To rename all .txt to .xml:



                  ls *.txt | parallel mv {} {.}.xml


                  Watch the intro video for GNU Parallel to learn more:
                  http://www.youtube.com/watch?v=OpaiGYxkSuQ







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Apr 30 '12 at 0:49









                  whitequark

                  13.1k53950




                  13.1k53950










                  answered Aug 22 '10 at 20:02









                  Ole TangeOle Tange

                  2,2801823




                  2,2801823























                      4














                      Just to add to CoverosGene's answer, here is a way to list just the directory names:



                      for f in */; do
                      echo "Directory -> $f"
                      done





                      share|improve this answer




























                        4














                        Just to add to CoverosGene's answer, here is a way to list just the directory names:



                        for f in */; do
                        echo "Directory -> $f"
                        done





                        share|improve this answer


























                          4












                          4








                          4







                          Just to add to CoverosGene's answer, here is a way to list just the directory names:



                          for f in */; do
                          echo "Directory -> $f"
                          done





                          share|improve this answer













                          Just to add to CoverosGene's answer, here is a way to list just the directory names:



                          for f in */; do
                          echo "Directory -> $f"
                          done






                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Feb 11 '15 at 14:36









                          n3on3o

                          1413




                          1413























                              1














                              Why not set IFS to a newline, then capture the output of ls in an array? Setting IFS to newline should resolve issues with funny characters in file names; using ls can be nice because it has a built-in sort facility.



                              (In testing I was having trouble setting IFS to n but setting it to newline backspace works, as suggested elsewhere here):



                              E.g. (assuming desired ls search pattern passed in $1):



                              SAVEIFS=$IFS
                              IFS=$(echo -en "nb")

                              FILES=($(/bin/ls "$1"))

                              for AFILE in ${FILES[@]}
                              do
                              ... do something with a file ...
                              done

                              IFS=$SAVEIFS


                              This is especially handy on OS X, e.g., to capture a list of files sorted by creation date (oldest to newest), the ls command is ls -t -U -r.






                              share|improve this answer





















                              • 2





                                Filenames can also contain newlines, and they often do when users are permitted to name their own files. Breaking on n is insufficient. The only valid solutions use a for loop with pathname expansion, or find.

                                – ghoti
                                Oct 17 '14 at 22:21











                              • The only reliable way to transfer a list of file names is to separate them with a NUL character, as this is the only one definitely not contained in a file path.

                                – glglgl
                                Jan 2 '15 at 9:23


















                              1














                              Why not set IFS to a newline, then capture the output of ls in an array? Setting IFS to newline should resolve issues with funny characters in file names; using ls can be nice because it has a built-in sort facility.



                              (In testing I was having trouble setting IFS to n but setting it to newline backspace works, as suggested elsewhere here):



                              E.g. (assuming desired ls search pattern passed in $1):



                              SAVEIFS=$IFS
                              IFS=$(echo -en "nb")

                              FILES=($(/bin/ls "$1"))

                              for AFILE in ${FILES[@]}
                              do
                              ... do something with a file ...
                              done

                              IFS=$SAVEIFS


                              This is especially handy on OS X, e.g., to capture a list of files sorted by creation date (oldest to newest), the ls command is ls -t -U -r.






                              share|improve this answer





















                              • 2





                                Filenames can also contain newlines, and they often do when users are permitted to name their own files. Breaking on n is insufficient. The only valid solutions use a for loop with pathname expansion, or find.

                                – ghoti
                                Oct 17 '14 at 22:21











                              • The only reliable way to transfer a list of file names is to separate them with a NUL character, as this is the only one definitely not contained in a file path.

                                – glglgl
                                Jan 2 '15 at 9:23
















                              1












                              1








                              1







                              Why not set IFS to a newline, then capture the output of ls in an array? Setting IFS to newline should resolve issues with funny characters in file names; using ls can be nice because it has a built-in sort facility.



                              (In testing I was having trouble setting IFS to n but setting it to newline backspace works, as suggested elsewhere here):



                              E.g. (assuming desired ls search pattern passed in $1):



                              SAVEIFS=$IFS
                              IFS=$(echo -en "nb")

                              FILES=($(/bin/ls "$1"))

                              for AFILE in ${FILES[@]}
                              do
                              ... do something with a file ...
                              done

                              IFS=$SAVEIFS


                              This is especially handy on OS X, e.g., to capture a list of files sorted by creation date (oldest to newest), the ls command is ls -t -U -r.






                              share|improve this answer















                              Why not set IFS to a newline, then capture the output of ls in an array? Setting IFS to newline should resolve issues with funny characters in file names; using ls can be nice because it has a built-in sort facility.



                              (In testing I was having trouble setting IFS to n but setting it to newline backspace works, as suggested elsewhere here):



                              E.g. (assuming desired ls search pattern passed in $1):



                              SAVEIFS=$IFS
                              IFS=$(echo -en "nb")

                              FILES=($(/bin/ls "$1"))

                              for AFILE in ${FILES[@]}
                              do
                              ... do something with a file ...
                              done

                              IFS=$SAVEIFS


                              This is especially handy on OS X, e.g., to capture a list of files sorted by creation date (oldest to newest), the ls command is ls -t -U -r.







                              share|improve this answer














                              share|improve this answer



                              share|improve this answer








                              edited Oct 22 '12 at 1:09









                              Indrek

                              20.6k117484




                              20.6k117484










                              answered May 10 '12 at 22:36









                              jetsetjetset

                              1294




                              1294








                              • 2





                                Filenames can also contain newlines, and they often do when users are permitted to name their own files. Breaking on n is insufficient. The only valid solutions use a for loop with pathname expansion, or find.

                                – ghoti
                                Oct 17 '14 at 22:21











                              • The only reliable way to transfer a list of file names is to separate them with a NUL character, as this is the only one definitely not contained in a file path.

                                – glglgl
                                Jan 2 '15 at 9:23
















                              • 2





                                Filenames can also contain newlines, and they often do when users are permitted to name their own files. Breaking on n is insufficient. The only valid solutions use a for loop with pathname expansion, or find.

                                – ghoti
                                Oct 17 '14 at 22:21











                              • The only reliable way to transfer a list of file names is to separate them with a NUL character, as this is the only one definitely not contained in a file path.

                                – glglgl
                                Jan 2 '15 at 9:23










                              2




                              2





                              Filenames can also contain newlines, and they often do when users are permitted to name their own files. Breaking on n is insufficient. The only valid solutions use a for loop with pathname expansion, or find.

                              – ghoti
                              Oct 17 '14 at 22:21





                              Filenames can also contain newlines, and they often do when users are permitted to name their own files. Breaking on n is insufficient. The only valid solutions use a for loop with pathname expansion, or find.

                              – ghoti
                              Oct 17 '14 at 22:21













                              The only reliable way to transfer a list of file names is to separate them with a NUL character, as this is the only one definitely not contained in a file path.

                              – glglgl
                              Jan 2 '15 at 9:23







                              The only reliable way to transfer a list of file names is to separate them with a NUL character, as this is the only one definitely not contained in a file path.

                              – glglgl
                              Jan 2 '15 at 9:23













                              -3














                              This is how I do it, but there are probably more efficient ways.



                              ls > filelist.txt

                              while read filename; do echo filename: "$filename"; done < filelist.txt





                              share|improve this answer



















                              • 6





                                Stick to pipes in place of the file: >ls | while read i; do echo filename: $i; done

                                – Jeremy L
                                Aug 28 '09 at 18:46











                              • Cool. I should say that you can also use $EDITOR filelist.txt in between the two commands. Lots of stuff you can do in an editor that is easier than on the command line. Not relevant to this question, though.

                                – TREE
                                Aug 31 '09 at 18:45











                              • Your solution doesn't at all address the problem with file names containing newlines and other fancy stuff.

                                – glglgl
                                Jan 2 '15 at 9:24
















                              -3














                              This is how I do it, but there are probably more efficient ways.



                              ls > filelist.txt

                              while read filename; do echo filename: "$filename"; done < filelist.txt





                              share|improve this answer



















                              • 6





                                Stick to pipes in place of the file: >ls | while read i; do echo filename: $i; done

                                – Jeremy L
                                Aug 28 '09 at 18:46











                              • Cool. I should say that you can also use $EDITOR filelist.txt in between the two commands. Lots of stuff you can do in an editor that is easier than on the command line. Not relevant to this question, though.

                                – TREE
                                Aug 31 '09 at 18:45











                              • Your solution doesn't at all address the problem with file names containing newlines and other fancy stuff.

                                – glglgl
                                Jan 2 '15 at 9:24














                              -3












                              -3








                              -3







                              This is how I do it, but there are probably more efficient ways.



                              ls > filelist.txt

                              while read filename; do echo filename: "$filename"; done < filelist.txt





                              share|improve this answer













                              This is how I do it, but there are probably more efficient ways.



                              ls > filelist.txt

                              while read filename; do echo filename: "$filename"; done < filelist.txt






                              share|improve this answer












                              share|improve this answer



                              share|improve this answer










                              answered Aug 28 '09 at 17:28









                              TREETREE

                              7171614




                              7171614








                              • 6





                                Stick to pipes in place of the file: >ls | while read i; do echo filename: $i; done

                                – Jeremy L
                                Aug 28 '09 at 18:46











                              • Cool. I should say that you can also use $EDITOR filelist.txt in between the two commands. Lots of stuff you can do in an editor that is easier than on the command line. Not relevant to this question, though.

                                – TREE
                                Aug 31 '09 at 18:45











                              • Your solution doesn't at all address the problem with file names containing newlines and other fancy stuff.

                                – glglgl
                                Jan 2 '15 at 9:24














                              • 6





                                Stick to pipes in place of the file: >ls | while read i; do echo filename: $i; done

                                – Jeremy L
                                Aug 28 '09 at 18:46











                              • Cool. I should say that you can also use $EDITOR filelist.txt in between the two commands. Lots of stuff you can do in an editor that is easier than on the command line. Not relevant to this question, though.

                                – TREE
                                Aug 31 '09 at 18:45











                              • Your solution doesn't at all address the problem with file names containing newlines and other fancy stuff.

                                – glglgl
                                Jan 2 '15 at 9:24








                              6




                              6





                              Stick to pipes in place of the file: >ls | while read i; do echo filename: $i; done

                              – Jeremy L
                              Aug 28 '09 at 18:46





                              Stick to pipes in place of the file: >ls | while read i; do echo filename: $i; done

                              – Jeremy L
                              Aug 28 '09 at 18:46













                              Cool. I should say that you can also use $EDITOR filelist.txt in between the two commands. Lots of stuff you can do in an editor that is easier than on the command line. Not relevant to this question, though.

                              – TREE
                              Aug 31 '09 at 18:45





                              Cool. I should say that you can also use $EDITOR filelist.txt in between the two commands. Lots of stuff you can do in an editor that is easier than on the command line. Not relevant to this question, though.

                              – TREE
                              Aug 31 '09 at 18:45













                              Your solution doesn't at all address the problem with file names containing newlines and other fancy stuff.

                              – glglgl
                              Jan 2 '15 at 9:24





                              Your solution doesn't at all address the problem with file names containing newlines and other fancy stuff.

                              – glglgl
                              Jan 2 '15 at 9:24


















                              draft saved

                              draft discarded




















































                              Thanks for contributing an answer to Super User!


                              • 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%2fsuperuser.com%2fquestions%2f31464%2flooping-through-ls-results-in-bash-shell-script%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

                              Сан-Квентин

                              8-я гвардейская общевойсковая армия

                              Алькесар