0

In ~/Desktop/a/ , I have folders (with names that have spaces) and txt files in the form:

100 description of project A
100_notes.txt
200 description of project B
200_notes.txt

What I would like:

100 description of project A
100 description of project A.txt
200 description of project B
200 description of project B.txt

This is the script so far:

#!/bin/bash
cd ~/Desktop/a/
for i in *; do
  mv "$i/${f%.txt}" "$i.txt";
done

I'm trying it with test files, and it renames the folder to have a .txt extension, which isn't what I want.

  • Looks like you are using the wrong quotes. You appear to have the correct open quote and the wrong close quote (both should be the same). – Cupcake Protocol Nov 11 '18 at 7:29
  • What is 100 description, 100_notes.txt, the name of? It's unclear what things are named here. Is the last comma part of the filename name? – Kusalananda Nov 11 '18 at 7:38
  • @CupcakeProtocol That's an immediate problem, but also notice that they are using an unset variable f in the mv command, and that removing the final .txt filename suffix won't remove the _notes bit. – Kusalananda Nov 11 '18 at 7:40
  • @Kusalananda, I've edited it to clarify. The commas aren't part of the names. 100 description is the folder name, 100_notes is the txt file name. – user10630009 Nov 11 '18 at 10:22
  • @Kusalananda, I thought that "f%" means "file"? Is that not the case? – user10630009 Nov 11 '18 at 10:25
1
#!/bin/sh

for notes in ./???_notes.txt
do
    if [ ! -f "$notes" ]; then
        continue
    fi

    num=${notes%_notes.txt}

    set -- "$num "*/
    if [ "$#" -gt 1 ]; then
        echo 'More than one directory found:' >&2
        printf '\t%s\n' "$@" >&2
        printf 'Skipping %s...\n' "$notes" >&2
        continue
    elif [ ! -d "$1" ]; then
        printf 'No directory matching "%s" found\n' "$num */" >&2
        printf 'Skipping %s...\n' "$notes" >&2
        continue
    fi

    printf 'Would rename "%s" into "%s"\n' "$notes" "${1%/}.txt"
    # mv -i "$notes" "${1%/}.txt"
done

This script would iterate over all the NNN_notes.txt files in the current directory. For each file, the number NNN (which may be any three-letter string) is extracted and used to detect any directory called NNN followed by a space and an arbitrary string.

If a single directory is found, the file is renamed accordingly (the actual renaming is commented out for safety). If more than one directory or no directory is found, a message pointing this out is displayed.

The parameter substitution ${variable%string} removes the string string from the end of the value of $variable. The set command, when used as in this script, sets the positional parameters, $1, $2, $3 etc. to the things matching the given filename globbing pattern (in this script, we want the pattern to match exactly one directory). The value $# is the number of such positional parameters.

The way I have written this script, it would be executable by both bash and /bin/sh. It does not use any "bashisms".

A bash-only version:

#!/bin/bash

shopt -s nullglob

for notes in ./???_notes.txt
do
    num=${notes%_notes.txt}

    dirs=( "$num "*/ )
    if [ "${#dirs[@]}" -gt 1 ]; then
        echo 'More than one directory found:' >&2
        printf '\t%s\n' "${dirs[@]}" >&2
        printf 'Skipping %s...\n' "$notes" >&2
        continue
    elif [ "${#dirs[@]}" -eq 0 ]; then
        printf 'No directory matching "%s" found\n' "$num */" >&2
        printf 'Skipping %s...\n' "$notes" >&2
        continue
    fi

    printf 'Would rename "%s" into "%s"\n' "$notes" "${dirs[0]%/}.txt"
    # mv -i "$notes" "${dirs[0]%/}.txt"
done

The biggest difference here is that we use a named array dirs to hold the possible expansions of the pattern "$num "*/ and that we use the nullglob shell option to make non-matching filename patterns expand to nothing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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