If statement to tell if a file is in a directory [duplicate]

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











up vote
1
down vote

favorite













This question already has an answer here:



  • Delete all files in a directory whose name do not match a line in a file list

    6 answers



I am writing a bash script and want it to tell me if the names of the files in a directory appear in a text file and if not, remove them.



Something like this:



counter = 1
numFiles = ls -1 TestDir/ | wc -l
while [$counter -lt $numFiles]
do
if [file in TestDir/ not in fileNames.txt]
then
rm file
fi
((counter++))
done


So what I need help with is the if statement that is still pseudo code.







share|improve this question














marked as duplicate by don_crissti, terdon♦ bash
Users with the  bash badge can single-handedly close bash questions as duplicates and reopen them as needed.

StackExchange.ready(function()
if (StackExchange.options.isMobile) return;

$('.dupe-hammer-message-hover:not(.hover-bound)').each(function()
var $hover = $(this).addClass('hover-bound'),
$msg = $hover.siblings('.dupe-hammer-message');

$hover.hover(
function()
$hover.showInfoMessage('',
messageElement: $msg.clone().show(),
transient: false,
position: my: 'bottom left', at: 'top center', offsetTop: -7 ,
dismissable: false,
relativeToBody: true
);
,
function()
StackExchange.helpers.removeMessages();

);
);
);
Mar 16 at 12:54


This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.














  • Variable assignments should not have any spaces around the = sign: counter=1 e.g. Also the values should be quoted if they contain spaces: foo="bar baz". To assign the output of a command to a variable, you need command substitution syntax: numFiles=$(ls -1 TestDir/ | wc -l).
    – NickD
    Mar 16 at 13:02











  • got the best answer here: stackoverflow.com/questions/49320683/…
    – J. Tate
    Mar 16 at 13:22










  • The best answer according to you...
    – don_crissti
    Mar 16 at 16:28










  • @J.Tate that answer is dangerous (the -f is risky) and will fail if your file names can be substrings of one another. So, if you have a file called fo in your list, that command will deleted afore, foobar, yolofoyo etc etc. Also, if the file list can't be read for whatever reason, that command will delete every file in the directory. I'm afraid it really isn't a good answer.
    – terdon♦
    Mar 16 at 16:39














up vote
1
down vote

favorite













This question already has an answer here:



  • Delete all files in a directory whose name do not match a line in a file list

    6 answers



I am writing a bash script and want it to tell me if the names of the files in a directory appear in a text file and if not, remove them.



Something like this:



counter = 1
numFiles = ls -1 TestDir/ | wc -l
while [$counter -lt $numFiles]
do
if [file in TestDir/ not in fileNames.txt]
then
rm file
fi
((counter++))
done


So what I need help with is the if statement that is still pseudo code.







share|improve this question














marked as duplicate by don_crissti, terdon♦ bash
Users with the  bash badge can single-handedly close bash questions as duplicates and reopen them as needed.

StackExchange.ready(function()
if (StackExchange.options.isMobile) return;

$('.dupe-hammer-message-hover:not(.hover-bound)').each(function()
var $hover = $(this).addClass('hover-bound'),
$msg = $hover.siblings('.dupe-hammer-message');

$hover.hover(
function()
$hover.showInfoMessage('',
messageElement: $msg.clone().show(),
transient: false,
position: my: 'bottom left', at: 'top center', offsetTop: -7 ,
dismissable: false,
relativeToBody: true
);
,
function()
StackExchange.helpers.removeMessages();

);
);
);
Mar 16 at 12:54


This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.














  • Variable assignments should not have any spaces around the = sign: counter=1 e.g. Also the values should be quoted if they contain spaces: foo="bar baz". To assign the output of a command to a variable, you need command substitution syntax: numFiles=$(ls -1 TestDir/ | wc -l).
    – NickD
    Mar 16 at 13:02











  • got the best answer here: stackoverflow.com/questions/49320683/…
    – J. Tate
    Mar 16 at 13:22










  • The best answer according to you...
    – don_crissti
    Mar 16 at 16:28










  • @J.Tate that answer is dangerous (the -f is risky) and will fail if your file names can be substrings of one another. So, if you have a file called fo in your list, that command will deleted afore, foobar, yolofoyo etc etc. Also, if the file list can't be read for whatever reason, that command will delete every file in the directory. I'm afraid it really isn't a good answer.
    – terdon♦
    Mar 16 at 16:39












up vote
1
down vote

favorite









up vote
1
down vote

favorite












This question already has an answer here:



  • Delete all files in a directory whose name do not match a line in a file list

    6 answers



I am writing a bash script and want it to tell me if the names of the files in a directory appear in a text file and if not, remove them.



Something like this:



counter = 1
numFiles = ls -1 TestDir/ | wc -l
while [$counter -lt $numFiles]
do
if [file in TestDir/ not in fileNames.txt]
then
rm file
fi
((counter++))
done


So what I need help with is the if statement that is still pseudo code.







share|improve this question















This question already has an answer here:



  • Delete all files in a directory whose name do not match a line in a file list

    6 answers



I am writing a bash script and want it to tell me if the names of the files in a directory appear in a text file and if not, remove them.



Something like this:



counter = 1
numFiles = ls -1 TestDir/ | wc -l
while [$counter -lt $numFiles]
do
if [file in TestDir/ not in fileNames.txt]
then
rm file
fi
((counter++))
done


So what I need help with is the if statement that is still pseudo code.





This question already has an answer here:



  • Delete all files in a directory whose name do not match a line in a file list

    6 answers









share|improve this question













share|improve this question




share|improve this question








edited Mar 16 at 12:28

























asked Mar 16 at 12:21









J. Tate

1064




1064




marked as duplicate by don_crissti, terdon♦ bash
Users with the  bash badge can single-handedly close bash questions as duplicates and reopen them as needed.

StackExchange.ready(function()
if (StackExchange.options.isMobile) return;

$('.dupe-hammer-message-hover:not(.hover-bound)').each(function()
var $hover = $(this).addClass('hover-bound'),
$msg = $hover.siblings('.dupe-hammer-message');

$hover.hover(
function()
$hover.showInfoMessage('',
messageElement: $msg.clone().show(),
transient: false,
position: my: 'bottom left', at: 'top center', offsetTop: -7 ,
dismissable: false,
relativeToBody: true
);
,
function()
StackExchange.helpers.removeMessages();

);
);
);
Mar 16 at 12:54


This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.






marked as duplicate by don_crissti, terdon♦ bash
Users with the  bash badge can single-handedly close bash questions as duplicates and reopen them as needed.

StackExchange.ready(function()
if (StackExchange.options.isMobile) return;

$('.dupe-hammer-message-hover:not(.hover-bound)').each(function()
var $hover = $(this).addClass('hover-bound'),
$msg = $hover.siblings('.dupe-hammer-message');

$hover.hover(
function()
$hover.showInfoMessage('',
messageElement: $msg.clone().show(),
transient: false,
position: my: 'bottom left', at: 'top center', offsetTop: -7 ,
dismissable: false,
relativeToBody: true
);
,
function()
StackExchange.helpers.removeMessages();

);
);
);
Mar 16 at 12:54


This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.













  • Variable assignments should not have any spaces around the = sign: counter=1 e.g. Also the values should be quoted if they contain spaces: foo="bar baz". To assign the output of a command to a variable, you need command substitution syntax: numFiles=$(ls -1 TestDir/ | wc -l).
    – NickD
    Mar 16 at 13:02











  • got the best answer here: stackoverflow.com/questions/49320683/…
    – J. Tate
    Mar 16 at 13:22










  • The best answer according to you...
    – don_crissti
    Mar 16 at 16:28










  • @J.Tate that answer is dangerous (the -f is risky) and will fail if your file names can be substrings of one another. So, if you have a file called fo in your list, that command will deleted afore, foobar, yolofoyo etc etc. Also, if the file list can't be read for whatever reason, that command will delete every file in the directory. I'm afraid it really isn't a good answer.
    – terdon♦
    Mar 16 at 16:39
















  • Variable assignments should not have any spaces around the = sign: counter=1 e.g. Also the values should be quoted if they contain spaces: foo="bar baz". To assign the output of a command to a variable, you need command substitution syntax: numFiles=$(ls -1 TestDir/ | wc -l).
    – NickD
    Mar 16 at 13:02











  • got the best answer here: stackoverflow.com/questions/49320683/…
    – J. Tate
    Mar 16 at 13:22










  • The best answer according to you...
    – don_crissti
    Mar 16 at 16:28










  • @J.Tate that answer is dangerous (the -f is risky) and will fail if your file names can be substrings of one another. So, if you have a file called fo in your list, that command will deleted afore, foobar, yolofoyo etc etc. Also, if the file list can't be read for whatever reason, that command will delete every file in the directory. I'm afraid it really isn't a good answer.
    – terdon♦
    Mar 16 at 16:39















Variable assignments should not have any spaces around the = sign: counter=1 e.g. Also the values should be quoted if they contain spaces: foo="bar baz". To assign the output of a command to a variable, you need command substitution syntax: numFiles=$(ls -1 TestDir/ | wc -l).
– NickD
Mar 16 at 13:02





Variable assignments should not have any spaces around the = sign: counter=1 e.g. Also the values should be quoted if they contain spaces: foo="bar baz". To assign the output of a command to a variable, you need command substitution syntax: numFiles=$(ls -1 TestDir/ | wc -l).
– NickD
Mar 16 at 13:02













got the best answer here: stackoverflow.com/questions/49320683/…
– J. Tate
Mar 16 at 13:22




got the best answer here: stackoverflow.com/questions/49320683/…
– J. Tate
Mar 16 at 13:22












The best answer according to you...
– don_crissti
Mar 16 at 16:28




The best answer according to you...
– don_crissti
Mar 16 at 16:28












@J.Tate that answer is dangerous (the -f is risky) and will fail if your file names can be substrings of one another. So, if you have a file called fo in your list, that command will deleted afore, foobar, yolofoyo etc etc. Also, if the file list can't be read for whatever reason, that command will delete every file in the directory. I'm afraid it really isn't a good answer.
– terdon♦
Mar 16 at 16:39




@J.Tate that answer is dangerous (the -f is risky) and will fail if your file names can be substrings of one another. So, if you have a file called fo in your list, that command will deleted afore, foobar, yolofoyo etc etc. Also, if the file list can't be read for whatever reason, that command will delete every file in the directory. I'm afraid it really isn't a good answer.
– terdon♦
Mar 16 at 16:39










3 Answers
3






active

oldest

votes

















up vote
3
down vote















Rather than saving the file list in a variable, loop over the names:



for name in TestDir/*; do
# the rest of the code
done


To test whether $name occurs in the fileNames.txt, use grep -q:



for name in TestDir/*; do
if ! grep -qxF "$name" fileNames.txt; then
echo rm "$name"
fi
done


The -F makes grep perform a string comparison rather than a regular expression match. With -q we get no output from grep, just an exit status that we can use with the if statement (true if the string was found but the exclamation mark inverts the sense of the test). The -x tells grep that the string $name needs to match a whole line, from start to finish, not just a part of a line.



I have protected the actual rm with echo. Run and make sure that the correct files gets deleted.



If the filenames are listed without the TestDir path, then change $name in the grep command to $name##*/:



for name in TestDir/*; do
if ! grep -qxF "$name##*/" fileNames.txt; then
echo rm "$name"
fi
done


This will look for the filename portion of the path in $name rather than the full path including TestDir.






share|improve this answer





























    up vote
    2
    down vote













    With zsh:



    expected=($(f)"$(<fileNames.txt)") || exit
    cd TestDir || exit
    actual=(*(D))
    superfluous=($actual:)
    if (($#superfluous))
    echo These files are not in the expected list:
    printf ' - %qn' $superfluous
    read -q '?Do you want to delete them? ' && rm -rf -- $superfluous






    share|improve this answer



























      up vote
      2
      down vote













      Here's a working version using your approach:



      #!/bin/bash
      fileList="$1"
      targetDir="$2"

      ## Read the list of files into an associative array
      declare -A filesInFile
      while IFS= read -r file; do
      filesInFile["$file"]=1
      done < "$fileList"

      ## Collect the files in the target dir
      filesInDir=("$targetDir"/*);

      for file in "$filesInDir[@]"; do
      file=$file##*/; # get the name of the file; remove path
      ## If this file has no entry in the array, delete
      if [[ -z "$filesInFile[$file]" ]]; then
      echo "rm $file"
      fi
      done


      Remove the echo to actually delete the files. Note that I am not checking if the number of files differs, since there didn't seem much point given that the number of files might be the same but you could still have files whose name isn't in the list.






      share|improve this answer



























        3 Answers
        3






        active

        oldest

        votes








        3 Answers
        3






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes








        up vote
        3
        down vote















        Rather than saving the file list in a variable, loop over the names:



        for name in TestDir/*; do
        # the rest of the code
        done


        To test whether $name occurs in the fileNames.txt, use grep -q:



        for name in TestDir/*; do
        if ! grep -qxF "$name" fileNames.txt; then
        echo rm "$name"
        fi
        done


        The -F makes grep perform a string comparison rather than a regular expression match. With -q we get no output from grep, just an exit status that we can use with the if statement (true if the string was found but the exclamation mark inverts the sense of the test). The -x tells grep that the string $name needs to match a whole line, from start to finish, not just a part of a line.



        I have protected the actual rm with echo. Run and make sure that the correct files gets deleted.



        If the filenames are listed without the TestDir path, then change $name in the grep command to $name##*/:



        for name in TestDir/*; do
        if ! grep -qxF "$name##*/" fileNames.txt; then
        echo rm "$name"
        fi
        done


        This will look for the filename portion of the path in $name rather than the full path including TestDir.






        share|improve this answer


























          up vote
          3
          down vote















          Rather than saving the file list in a variable, loop over the names:



          for name in TestDir/*; do
          # the rest of the code
          done


          To test whether $name occurs in the fileNames.txt, use grep -q:



          for name in TestDir/*; do
          if ! grep -qxF "$name" fileNames.txt; then
          echo rm "$name"
          fi
          done


          The -F makes grep perform a string comparison rather than a regular expression match. With -q we get no output from grep, just an exit status that we can use with the if statement (true if the string was found but the exclamation mark inverts the sense of the test). The -x tells grep that the string $name needs to match a whole line, from start to finish, not just a part of a line.



          I have protected the actual rm with echo. Run and make sure that the correct files gets deleted.



          If the filenames are listed without the TestDir path, then change $name in the grep command to $name##*/:



          for name in TestDir/*; do
          if ! grep -qxF "$name##*/" fileNames.txt; then
          echo rm "$name"
          fi
          done


          This will look for the filename portion of the path in $name rather than the full path including TestDir.






          share|improve this answer
























            up vote
            3
            down vote










            up vote
            3
            down vote











            Rather than saving the file list in a variable, loop over the names:



            for name in TestDir/*; do
            # the rest of the code
            done


            To test whether $name occurs in the fileNames.txt, use grep -q:



            for name in TestDir/*; do
            if ! grep -qxF "$name" fileNames.txt; then
            echo rm "$name"
            fi
            done


            The -F makes grep perform a string comparison rather than a regular expression match. With -q we get no output from grep, just an exit status that we can use with the if statement (true if the string was found but the exclamation mark inverts the sense of the test). The -x tells grep that the string $name needs to match a whole line, from start to finish, not just a part of a line.



            I have protected the actual rm with echo. Run and make sure that the correct files gets deleted.



            If the filenames are listed without the TestDir path, then change $name in the grep command to $name##*/:



            for name in TestDir/*; do
            if ! grep -qxF "$name##*/" fileNames.txt; then
            echo rm "$name"
            fi
            done


            This will look for the filename portion of the path in $name rather than the full path including TestDir.






            share|improve this answer
















            Rather than saving the file list in a variable, loop over the names:



            for name in TestDir/*; do
            # the rest of the code
            done


            To test whether $name occurs in the fileNames.txt, use grep -q:



            for name in TestDir/*; do
            if ! grep -qxF "$name" fileNames.txt; then
            echo rm "$name"
            fi
            done


            The -F makes grep perform a string comparison rather than a regular expression match. With -q we get no output from grep, just an exit status that we can use with the if statement (true if the string was found but the exclamation mark inverts the sense of the test). The -x tells grep that the string $name needs to match a whole line, from start to finish, not just a part of a line.



            I have protected the actual rm with echo. Run and make sure that the correct files gets deleted.



            If the filenames are listed without the TestDir path, then change $name in the grep command to $name##*/:



            for name in TestDir/*; do
            if ! grep -qxF "$name##*/" fileNames.txt; then
            echo rm "$name"
            fi
            done


            This will look for the filename portion of the path in $name rather than the full path including TestDir.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Mar 16 at 13:08

























            answered Mar 16 at 12:34









            Kusalananda

            103k13201317




            103k13201317






















                up vote
                2
                down vote













                With zsh:



                expected=($(f)"$(<fileNames.txt)") || exit
                cd TestDir || exit
                actual=(*(D))
                superfluous=($actual:)
                if (($#superfluous))
                echo These files are not in the expected list:
                printf ' - %qn' $superfluous
                read -q '?Do you want to delete them? ' && rm -rf -- $superfluous






                share|improve this answer
























                  up vote
                  2
                  down vote













                  With zsh:



                  expected=($(f)"$(<fileNames.txt)") || exit
                  cd TestDir || exit
                  actual=(*(D))
                  superfluous=($actual:)
                  if (($#superfluous))
                  echo These files are not in the expected list:
                  printf ' - %qn' $superfluous
                  read -q '?Do you want to delete them? ' && rm -rf -- $superfluous






                  share|improve this answer






















                    up vote
                    2
                    down vote










                    up vote
                    2
                    down vote









                    With zsh:



                    expected=($(f)"$(<fileNames.txt)") || exit
                    cd TestDir || exit
                    actual=(*(D))
                    superfluous=($actual:)
                    if (($#superfluous))
                    echo These files are not in the expected list:
                    printf ' - %qn' $superfluous
                    read -q '?Do you want to delete them? ' && rm -rf -- $superfluous






                    share|improve this answer












                    With zsh:



                    expected=($(f)"$(<fileNames.txt)") || exit
                    cd TestDir || exit
                    actual=(*(D))
                    superfluous=($actual:)
                    if (($#superfluous))
                    echo These files are not in the expected list:
                    printf ' - %qn' $superfluous
                    read -q '?Do you want to delete them? ' && rm -rf -- $superfluous







                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered Mar 16 at 12:46









                    Stéphane Chazelas

                    280k53515847




                    280k53515847




















                        up vote
                        2
                        down vote













                        Here's a working version using your approach:



                        #!/bin/bash
                        fileList="$1"
                        targetDir="$2"

                        ## Read the list of files into an associative array
                        declare -A filesInFile
                        while IFS= read -r file; do
                        filesInFile["$file"]=1
                        done < "$fileList"

                        ## Collect the files in the target dir
                        filesInDir=("$targetDir"/*);

                        for file in "$filesInDir[@]"; do
                        file=$file##*/; # get the name of the file; remove path
                        ## If this file has no entry in the array, delete
                        if [[ -z "$filesInFile[$file]" ]]; then
                        echo "rm $file"
                        fi
                        done


                        Remove the echo to actually delete the files. Note that I am not checking if the number of files differs, since there didn't seem much point given that the number of files might be the same but you could still have files whose name isn't in the list.






                        share|improve this answer
























                          up vote
                          2
                          down vote













                          Here's a working version using your approach:



                          #!/bin/bash
                          fileList="$1"
                          targetDir="$2"

                          ## Read the list of files into an associative array
                          declare -A filesInFile
                          while IFS= read -r file; do
                          filesInFile["$file"]=1
                          done < "$fileList"

                          ## Collect the files in the target dir
                          filesInDir=("$targetDir"/*);

                          for file in "$filesInDir[@]"; do
                          file=$file##*/; # get the name of the file; remove path
                          ## If this file has no entry in the array, delete
                          if [[ -z "$filesInFile[$file]" ]]; then
                          echo "rm $file"
                          fi
                          done


                          Remove the echo to actually delete the files. Note that I am not checking if the number of files differs, since there didn't seem much point given that the number of files might be the same but you could still have files whose name isn't in the list.






                          share|improve this answer






















                            up vote
                            2
                            down vote










                            up vote
                            2
                            down vote









                            Here's a working version using your approach:



                            #!/bin/bash
                            fileList="$1"
                            targetDir="$2"

                            ## Read the list of files into an associative array
                            declare -A filesInFile
                            while IFS= read -r file; do
                            filesInFile["$file"]=1
                            done < "$fileList"

                            ## Collect the files in the target dir
                            filesInDir=("$targetDir"/*);

                            for file in "$filesInDir[@]"; do
                            file=$file##*/; # get the name of the file; remove path
                            ## If this file has no entry in the array, delete
                            if [[ -z "$filesInFile[$file]" ]]; then
                            echo "rm $file"
                            fi
                            done


                            Remove the echo to actually delete the files. Note that I am not checking if the number of files differs, since there didn't seem much point given that the number of files might be the same but you could still have files whose name isn't in the list.






                            share|improve this answer












                            Here's a working version using your approach:



                            #!/bin/bash
                            fileList="$1"
                            targetDir="$2"

                            ## Read the list of files into an associative array
                            declare -A filesInFile
                            while IFS= read -r file; do
                            filesInFile["$file"]=1
                            done < "$fileList"

                            ## Collect the files in the target dir
                            filesInDir=("$targetDir"/*);

                            for file in "$filesInDir[@]"; do
                            file=$file##*/; # get the name of the file; remove path
                            ## If this file has no entry in the array, delete
                            if [[ -z "$filesInFile[$file]" ]]; then
                            echo "rm $file"
                            fi
                            done


                            Remove the echo to actually delete the files. Note that I am not checking if the number of files differs, since there didn't seem much point given that the number of files might be the same but you could still have files whose name isn't in the list.







                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered Mar 16 at 12:50









                            terdon♦

                            122k28229400




                            122k28229400












                                Popular posts from this blog

                                Peggy Mitchell

                                The Forum (Inglewood, California)

                                Palaiologos