2

I have written the following Bash script. Its role is to check its own name, and in case of nonexistent extension , to amend ".sh" with sed. Still I have error "missing target file..."

#!/bin/bash

FILE_NAME="$0"
EXTENSION=".sh"
FILE_NAME_MOD="$FILE_NAME$EXTENSION"

if [[ "$0" != "FILE_NAME_MOD" ]]; then
    
    echo mv -v "$FILENAME" "$FILENAME$EXTENSION"

    
    cp "$0" | sed 's/\([^.sh]\)$/\1.sh/g' $0

fi
  • `[^.sh]` matches a single char that is not a `.`, `s` and `h`. Try `sed '/\.sh$/b;s/$/.sh/' "$0"` or `sed '/\.sh$/!s/$/.sh/' "$0"` – Wiktor Stribiżew Mar 20 '21 at 21:25
  • The condition is kinda wrong. $0 will never equal to FILE_NAME_MOD this way since FILE_NAME_MOD is set to $0 + EXTENSION. Thus it always evaluates to true and tries to copy even files ending with the expected extension. Which is probably not what you want. – Mirronelli Mar 20 '21 at 21:32
  • And the `cp ... | sed ...` pipeline makes no sense. `cp` expects 2 filenames, a source and a destination, and doesn't print anything to stdout (i.e. to the pipe). Meanwhile, `sed ... $0` will read *the contents of* the script, apply its change to those contents, and print the modified script to its stdout (probably the terminal). – Gordon Davisson Mar 20 '21 at 22:19

3 Answers3

2
#!/bin/bash

file="$0"
extension=".sh"

if [ $(echo -n $file | tail -c 3) != $extension ]; then
    mv -v "$file" "$file$extension"
fi

Important stuff:

  • -n flag suppress the new line at the end, so we can test for 3 chars instead of 4
  • When in doubt, always use set -x to debug your scripts.
Niloct
  • 9,491
  • 3
  • 44
  • 57
  • 1
    What about when they want a different extension - replace `tail -c 3` with `tail -c ${#extension}`.. – Mr R Mar 21 '21 at 06:43
  • 1
    Or do it all in the shell (no subshells / commands) .. `if [ "${file:(-${#extension})}`" != "$extension" ]; – Mr R Mar 21 '21 at 06:48
  • Many ways to solve this problem indeed @MrR :) My first try was similar to yours, but I used `${file:-3}` and forgot that the `-` means default value when not wrapped in parenthesis... :) Congrats on the cleaner solution. Post your solution to get upvotes. – Niloct Mar 21 '21 at 15:11
  • 1
    this is the ref I always go to - https://tldp.org/LDP/abs/html/ - @Niloct – Mr R Mar 21 '21 at 19:24
  • 1
    @MrR The Advanced Bash-Scripting Guide has a really bad reputation for teaching sloppy scripting practices, like not double-quoting variable references (which [can cause problems](https://stackoverflow.com/questions/55023461/when-should-i-double-quote-a-parameter-expansion). [Greycat's BashFAQ](http://mywiki.wooledge.org/BashFAQ) is a much better reference. – Gordon Davisson Mar 23 '21 at 07:27
  • what is the difference between "$file" and $file? – Wojciech Mierzejewski Mar 23 '21 at 20:25
  • Double quotes preserve whitespace on its content. – Niloct Mar 23 '21 at 20:28
  • @WojciechMierzejewski See [this](https://stackoverflow.com/questions/29378566/i-just-assigned-a-variable-but-echo-variable-shows-something-else) and [this](https://stackoverflow.com/questions/63038841/why-the-assignment-of-an-array-string-with-brackets-to-environment-variable-is) and [this](https://unix.stackexchange.com/questions/131766/why-does-my-shell-script-choke-on-whitespace-or-other-special-characters) and [this](https://stackoverflow.com/questions/53945071/why-testing-for-first-character-skips-empty-lines) and ... It's amazing how often the solution to a problem is "double-quote it". – Gordon Davisson Mar 23 '21 at 22:55
1

Try this Shellcheck-clean code:

#! /bin/bash -p

file=${BASH_SOURCE[0]}
extension=.sh

[[ $file == *"$extension" ]] || mv -i -- "$file" "$file$extension"
pjh
  • 6,388
  • 2
  • 16
  • 17
0

Just for fun, here is a way to do it just with GNU sed:

#!/usr/bin/env bash

sed --silent '
# match FILENAME only if it does not end with ".sh"
/\.sh$/! {
    # change "FILENAME" to "mv -v FILENAME FILENAME.sh"
    s/.*/mv -v & &.sh/
    # execute the command
    e
}
' <<<"$0"

You can also make the above script output useful messages:

#!/usr/bin/env bash

sed --silent '
/\.sh$/! {
    s/.*/mv -v & &.sh/
    e
    # exit with code 0 immediately after the change has been made
    q0
}
# otherwise exit with code 1
q1
' <<<"$0" && echo 'done' || echo 'no changes were made'
Fritjof Larsson
  • 142
  • 2
  • 4
  • 6