1

I have a bash script which reads a file having multiple line of text and executes a command on those lines. Sometimes the command work, but sometimes it fails. I want to append a pound sign (#) at the start of the lines of text where the command worked successfully and lines where the command failed they should as it is so that they can be retried in next run of the script. Lines which starts from # doesn't get executed.
My current script looks something like this -

COUNT=1
while read -r LINE; do
    if [[ ($LINE != \#*) && !(-z "${LINE// }") ]] ; then
        echo "${LINE}"
        # execute "$LINE"
        if [ $? -eq 0 ]; then
            echo OK # Append pound (#) at the start of the line
        else
            echo FAIL # Keep the line in the source file
        fi
    fi
    COUNT=$(( $COUNT + 1 ))
done < $SRC_FILE

As you can see the condition is already there, I just need to replace those echo OK and echo FAIL line of code. I tried this answer, but it removes all the lines in the end, I don't want that, I want to keep the failed lines in the file.

I tried using sed, but it appear to do nothing -

sed -n "${COUNT}s/^/#/" $SRC_FILE >> $SRC_FILE

Is there any way to do that?

PS: I want to make it work on bash and zsh on both Linux and Mac terminal, especially Mac zsh.

Update:
I tried the following command

sed "${COUNT}s/.*/#&/" $SRC_FILE >$SRC_FILE

It removes all the line from the source file and make it empty, but if I change the output file, it works, although adds the # in the last line only, since it's still reading from the original source file.

  • Is overall goal that you want to add a # at the start of non-empty lines that does not already have a # at the start of the line? – Kusalananda May 8 at 15:46
  • @Kusalananda My goal is to add # at the start of lines which succeed in running the command so that I can skip them in the next run. Lines which already have # in start are anyway not processed. – noob May 8 at 15:51
1

You could do:

#! /bin/sh -
src_file=/path/to/file
{
  while read <&3 -r line; do
    case $line in
      ('#'*) ;;
      (*)
         if eval " $line" 3<&- 4>&-; then
           echo OK
           line="# $line"
         else
           echo FAIL
         fi
    esac
    printf '%s\n' "$line" >&4
  done
  mv -- "$src_file.new" "$src_file"
} 3< "$src_file" 4> "$src_file.new"

Each line of the file will be evaluated as sh code. Replace sh with bash or zsh if those lines use bash-specific or zsh-specific features.

Here, we're generating a file.new file which we rename to file in the end. That's more or less what sed -i (GNU) or sed -i '' (FreeBSD/macOS) do as well. Generally, you can't easily write to a given file at the same as you are reading it.

0

Using sed [command] file > file will, as you have discovered, not do what you want. The redirection wipes out the contents of file and replaces it with the output of sed.

What you need to do is use sed's built-in ability to modify files in-place:

sed --in-place "$COUNTs/^/#/" file

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.