4

My code works, but not in the way I want to exactly. Basically, what my code does is that it looks through the current directory, searches for folders, and within those folders it looks at the files. If one of the file extensions is the value of the $Example variable, then it should delete all other files with the same beginning file name, regardless of extension and rename the one with the $Example extension to the same name, just without the $Example extension. Here is the code:

#!/bin/sh
set -o errexit
Example=dummy
for d in *; do
    if test "$(ls -A "$d" 2>/dev/null)"; then
        if [ $(ls -1 ${d}/*.$Example 2>/dev/null | wc -l) -ge 1 ]; then
            cd $(pwd)/$d;
            for f in *.$Example; do
                fileName="${f%.$Example}";
                mv "$f" "${f%.$Example}";
                #tr "\r" "\n" < "${f%.$Example}" > "${f%.$Example}"
                listToDelete=$(find -name "$fileName.*");
                for g in $listToDelete; do
                    rm $g;
                done;
            done;
            cd ..;
        fi;
    fi;
done

The files being used have been created in VIM, so are supposed to have Linux formatting, rather than Windows formatting. For some reason or other, once the extension has been stripped, using this code, it gets formatted with \r, and the file fails to run. I added the comment where my temporary solution is located, but I was wondering if either there is some way to alter the mv function to keep the Linux formatting, or maybe there is another way to achieve what I want. Thanks

Dan Lowe
  • 51,713
  • 20
  • 123
  • 112
  • 3
    `mv` should not be changing file contents, no matter how you called it. – Dan Lowe Jul 11 '17 at 19:25
  • 1
    How do you check that file has `\r` instead of `\n`? – Arkadiusz Drabczyk Jul 11 '17 at 19:26
  • 1
    I cannot reproduce this issue on my system and I believe that your problem lies elsewhere. – Arkadiusz Drabczyk Jul 11 '17 at 19:27
  • 2
    FWIW, `\r` line endings are not Windows formatting, Windows would use `\r\n`. – Dan Lowe Jul 11 '17 at 19:29
  • @ArkadiuszDrabczyk When i use the framework (jenkins) to run the script, it states the directory of the file, and ending it with: line x: $'\r': command not found. What is curious, is that before running the script provided in the body, this issue never came up. – ConfuzzledandPuzzled Jul 11 '17 at 19:29
  • @ConfuzzledandPuzzled: I see, so the problem is not in the file *contents* but in the file *name*? – Arkadiusz Drabczyk Jul 11 '17 at 19:34
  • 2
    It may be that your script has a `\r` in it somewhere, and is putting that on the filenames. I have to say your script is rather complex for what it is doing: `for file in $( find . -type f -name \*.${Example} ); do root_name=${file%.$Example}; mv $file tmp$$; rm ${root_name}*; mv tmp$$ ${root_name}; done` – Jack Jul 11 '17 at 19:34
  • I figured it would be a similar issue to http://stackoverflow.com/questions/11616835/r-command-not-found-bashrc-bash-profile but I am wondering if I can avoid writing 'tr "\r" "\n" < "${f%.$Example}" > "${f%.$Example}"' – ConfuzzledandPuzzled Jul 11 '17 at 19:35
  • 1
    First, run `dos2unix `. – Jack Jul 11 '17 at 19:36
  • @ArkadiuszDrabczyk I mean the contents should not be altered, but according to the error, it finds a '\r', when it was not present before. The extensions are removed as I want them to, but I would like to know if this script is causing the '\r' to come up. I am going to type out the target file character for character to see if it was a copying issue. – ConfuzzledandPuzzled Jul 11 '17 at 19:37
  • 1
    @ConfuzzledandPuzzled: Now I see, it's about file *contets*. Similarly to [Jack](https://stackoverflow.com/users/2584475/jack) `dos2unix` was my first thought but it would be nice to find a real reason for which this problem occurs. Could you compare `md5sum` of the files before running the script and after running it? They should be different if the contents was changed. – Arkadiusz Drabczyk Jul 11 '17 at 19:39
  • @ArkadiuszDrabczyk Yes, the md5sum values are different. Which means that the files are changed. In my opinion, this does not make sense, since (at least to me) the contents of the files remain unchanged. At first I thought it was an issue since i forgot to comment out 'tr "\r" "\n" < "${f%.$Example}" > "${f%.$Example}"' but once I did, the results were still different. – ConfuzzledandPuzzled Jul 11 '17 at 19:50
  • 1
    @ConfuzzledandPuzzled: does the same happen when you perform `mv` operation *outside* of the script? – Arkadiusz Drabczyk Jul 11 '17 at 19:51
  • @ArkadiuszDrabczyk If i use the terminal to perform 'mv' then the md5sum remains the same. My username clearly fits this situation imo – ConfuzzledandPuzzled Jul 11 '17 at 19:54
  • 1
    @ConfuzzledandPuzzled: ok, we excluded an obscure bug in `mv`. Make a backup of your script and run `dos2unix` on it and compare `md5sum` of the two files - do they differ? – Arkadiusz Drabczyk Jul 11 '17 at 19:55
  • 1
    @ConfuzzledandPuzzled: and also add `set -x` just under the `#/bin/bash` and run `mv` command exactly as it's shown as done by the script. – Arkadiusz Drabczyk Jul 11 '17 at 20:00
  • @ArkadiuszDrabczyk I found the issue, and I am embarrassed. I overlooked the source file itself. There are 4 copies of the file that have slight differences (where the $example fits in) and I have been referencing one of the files. I checked one other because the file happened to be more convenient to click on, and '#! /bin/sh' was not present in the file. I am doing a test right now to check if everything works now. – ConfuzzledandPuzzled Jul 11 '17 at 20:13
  • Thank you for helping me find my mistake! – ConfuzzledandPuzzled Jul 11 '17 at 20:18
  • 1
    @ConfuzzledandPuzzled: but what was it? I'm not sure how a lack of the shebang line would cause problems you describe. – Arkadiusz Drabczyk Jul 11 '17 at 20:20
  • @ArkadiuszDrabczyk That honestly was it. Looking back, it seems that the fact that it did not have the shebang line made it compile or run (or whatever the technical term is) properly how it was supposed to (and maybe told the compiler what formatting to work in?) and the `tr "\r" "\n" < "${f%.$Example}" > "${f%.$Example}" ` forced it to be formatted to be able to be read by the compiler. I checked the md5sum after i ran my script with the updated files to use the shebang line, and the values are identical – ConfuzzledandPuzzled Jul 11 '17 at 20:25
  • If that does make sense. – ConfuzzledandPuzzled Jul 11 '17 at 20:28
  • 1
    Note that the semicolons after `done` and `fi` are not needed and are conventionally omitted. – Jonathan Leffler Jul 11 '17 at 21:06
  • Improving the comment of @Jack. A more simplified one liner would be `for f in $(find . -type f -name "*.$Example"); do mv "$f" "${f%.$Example}" && rm "${f%.$Example}"?*; done` – Anubis Jul 31 '17 at 06:51
  • Consider running this through http://shellcheck.net/ and fixing what it finds. `ls` output is intended for human consumption, not programmatic use -- see http://mywiki.wooledge.org/ParsingLs -- and output from `find` likewise can't be safely iterated over after being stored in a string (because the same set of characters are valid in both C strings and filenames, so there's no 100%-reliable way to know where the division between subsequent names is); this is also covered in [BashPitfalls #1](http://mywiki.wooledge.org/BashPitfalls#for_i_in_.24.28ls_.2A.mp3.29). – Charles Duffy Nov 11 '17 at 00:56
  • Also, unquoted expansions are (extremely!) unsafe in general. If you have a file created with `touch '*'`, you don't want `rm $g` to expand the wildcard and delete everything in your local directory. – Charles Duffy Nov 11 '17 at 00:58

2 Answers2

1

The files being used have been created in VIM, so are supposed to have Linux formatting, rather than Windows formatting.

That has no impact on the line separator being used.

But I can think of 3 different possible causes.

ViM substitution

The \r and \n may have been mixed up or the Windows line separator (\r\n) may have been striped out incorrectly. If you're just trying to convert the Windows line separator at some point, convert them to to Unix line separated files with dos2unix instead of sed or vim magic if possible and then edit them. If the files are adding or replacing line separators using ViM, remember that in ViM line separators are searched for using \n and replaced with \r since ViM just loads file data into a buffer. e.g. %s/\n/somestring/ and %s/somestring/\r/g.

Cygwin

If you're using Cygwin, I'm pretty sure it defaults to ViM using Windows line separators, but you can change it to Unix line separators. Don't remember how to off the top of my head though.

ViM default line separators

Not sure how this would have happened if you're on a GNU/Linux system, but the default line separator to be used in ViM can be specified to Unix using :e ++ff=unix

kurczynski
  • 359
  • 9
  • 17
0

This answer does not address the issue with a possible stray \r in the script source code, but rather gives an alternative way of doing what I believe is what the user wants to achieve:

#!/bin/bash

ext='dummy'
tmpfile=$(mktemp)

find . -type f -name "*.$ext" \
    -execdir bash -x -c \
        'cp "$3" "$2" &&
         tee "${3%.$1}"* <"$2" >/dev/null' bash "$ext" "$tmpfile" {} \;

rm -f "$tmpfile"

This finds all files with the given extension and for each of them executes

cp "$3" "$2" && tee "${3%.$1}"* <"$2" >/dev/null
  • $1 will be the extension without the dot.
  • $2 will be the name of a temporary file.
  • $3 will be the found file.

The command will first copy the found file to a temporary file, then it will feed this temporary file through tee which will duplicate its contents to all files with the same prefix (${3%.$1} will strip the extension from the found filename and ${3%.$1}* will expand to all files in the same directory that has the same prefix).

Most modern implementations of find supports -execdir which works like -exec with the difference that the given utility will be executed in the directory of the found name. Also, {} will be the pathless basename of the found thing.

Given the following files in a directory:

$ ls test/
f1.bar   f1.foo   f2.bar   f2.foo   f3.bar   f3.foo
f1.dummy f1.txt   f2.dummy f2.txt   f3.dummy f3.txt

This script does the following:

$ bash -x script.sh
+ ext=dummy
++ mktemp
+ tmpfile=/tmp/tmp.9v5JMAcA12
+ find . -type f -name '*.dummy' -execdir bash -x -c 'cp "$3" "$2" &&
         tee "${3%.$1}"* <"$2" >/dev/null' sh dummy /tmp/tmp.9v5JMAcA12 '{}' ';'
+ cp f1.dummy /tmp/tmp.9v5JMAcA12
+ tee f1.bar f1.dummy f1.foo f1.txt
+ cp f2.dummy /tmp/tmp.9v5JMAcA12
+ tee f2.bar f2.dummy f2.foo f2.txt
+ cp f3.dummy /tmp/tmp.9v5JMAcA12
+ tee f3.bar f3.dummy f3.foo f3.txt
+ rm -f /tmp/tmp.9v5JMAcA12

Remove -x from the invocation of bash (and from bash on the command line) to disable tracing.


I used bash rather than sh because my sh can't grok ${3%.$1} properly.

Kusalananda
  • 14,885
  • 3
  • 41
  • 52