pure-bash-bible icon indicating copy to clipboard operation
pure-bash-bible copied to clipboard

Include prepend function

Open helpermethod opened this issue 5 years ago • 8 comments

Include prepend function as described here.

prepend() {
  printf '%s%s' "$1" "$(< "$2")" > "$2"
}

helpermethod avatar Sep 25 '19 10:09 helpermethod

hey, this is innovative and fantastic! can you explain how it works, too? looking at how to prepend stdin to file.... I find the function no workie for me

# declare -f prepend
prepend ()
{
    printf '%s%s' "$1" "(< $2)" > "$2"
}
# echo world >zz
# prepend hello zz
# cat zz
hello(< zz)# bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)

georgalis avatar Sep 25 '19 16:09 georgalis

Basically:

  • Print the string that will be prepended.
  • Print the contents of the file.
  • Redirect this combined output to the file.

Notes:

  • $(< "$2") is equivalent to $(cat "$2").
  • This reading and writing of the same file is safe due to the $(< "$2") being evaluated first. (I could be wrong here, however I don't see it working any other way).

dylanaraps avatar Sep 25 '19 16:09 dylanaraps

@georgalis Hi, I've somehow missed a $ that's probably why the function didn't work for you. @dylanaraps explains the function better than I ever could.

Example use case:

Prepending the JIRA ID to the commit message within a commit hook within a prepare-commit-msg hook

https://github.com/helpermethod/awesome-git-hooks/blob/master/prepare-commit-msg.d/prepend-issue-key.sh

This reading and writing of the same file is safe due to the $(< "$2") being evaluated first. (I could be wrong here, however I don't see it working any other way).

@dylanaraps This is exactly how it works!

helpermethod avatar Sep 25 '19 16:09 helpermethod

I would also quote the $2 inside $(< $2), so $(< "$2").

The only issue with this method is that it'll be slow for large files. bash has to load the entire file as a string and then pass it to printf and finally pipe it back to the file.

I'm looking into whether or not there's a more performant method of doing this. :+1:

dylanaraps avatar Sep 25 '19 16:09 dylanaraps

...yes thank you both. much improved. not sure best way for performance and to fail safely, I normally use tmp files.

my use case has a bug that doesn't preserve trailing \n,

prepend () { printf '%s%s' "$1" "$(< $2)" > "$2" ;}
seq 1 5 >zz ; echo "hello" | prepend "$(cat)" zz

in this case two \n are missing from the final file.

georgalis avatar Sep 25 '19 16:09 georgalis

That is "fixable" by changing %s%s to %s\n%s\n though not ideal since it'll add a newline where one already exists.

dylanaraps avatar Sep 25 '19 17:09 dylanaraps

yes, I started to offer that, but it also adds a newline when there shouldn't be any!

georgalis avatar Sep 25 '19 17:09 georgalis

I would also quote the $2 inside $(< $2), so $(< "$2").

The only issue with this method is that it'll be slow for large files. bash has to load the entire file as a string and then pass it to printf and finally pipe it back to the file.

I'm looking into whether or not there's a more performant method of doing this. 👍

I suppose using sed would be the most performant solution for this task.

helpermethod avatar Sep 26 '19 15:09 helpermethod