-1

The actions I would like to do with this script is to

  1. Read the name (without base) of each file in a directory
  2. For each file, ask the user if he wants to perform an action (e.g. move the file in a directory)
  3. Perform or not that action.

    I have problem with while loop and read input. I tried with this:

    #! /bin/csh -f set DIR = 'pwd' find $DIR -name * -exec basename {} \; | while read filename do read -p $filename" (y or n?)" choice if ($choice = "y") then <> endif end

but I get the error:

find: `basename' terminated by signal 13.

I was thinking also to expand the input of the user using again a while until yes or no are given but it's a second order problem.

Rui F Ribeiro
  • 56,709
  • 26
  • 150
  • 232

1 Answers1

5

Part of your shell code looks like bash code even though you use a #!-line specifying csh. csh does not have a read command, and this is why you get a signal 13 error.

Signal 13 is the PIPE signal, and it gets sent to a process when it's trying to write to a dead pipe (a pipe that nobody is reading from the other end of). The pipe is dead because the read call failed in the while-loop. It failed since it's not available in csh.

If csh had had a read command, your loop would get filenames, but you would not have had any way of knowing where those filenames belonged (in what subdirectory) since you removed the directory path from them. You would therefore not be able to do much with them.


The easiest way for doing this would be to have find ask the question to the user:

find . -type f -ok some-action '{}' ';'

This would ask for confirmation for each pathname before executing the some-action utility with the pathname as its argument. It would ask with the complete command line that would be executed for the given pathname, so it's not quite what you might have in mind.

Note also that specifying -type f will find all regular files, while your -name '*' test will always be true for all types of files, including directories. I'm also using . (the current directory) for the top-level directory for find, which is what you seem to want to do with your DIR variable.

To manually ask the user for input on each file, and at the same time handle pathnames correctly, the easiest would be to do something like

find . -type f -exec sh -c '
    for pathname do
        printf "process %s (%s)?\n" "${pathname##*/}" "$pathname" >&2
        read answer
        case $answer in [Nn]*) continue; esac

        printf "processing %s...\n" "$pathname" >&2
        # process "$pathname" here
    done' sh {} +

This assumes a sh-like shell, and will spawn sh -c for batches of pathnames found by find. The internal shell script would ask the user whether they'd like to process each individual pathname or not. If the user answers with a word starting with n (case insensitive), the next pathname would be considered.

The "${pathname##*/}" bit in the code is equivalent to "$( basename "$pathname" )" as it removes everything in $pathname up to and including the last slash.

The find command acts as a sort of generator of pathnames for the for-loop in the internal script, so piping pathnames or filenames to a while-loop is never necessary.

Related:

Kusalananda
  • 333,661