4

I have the following line of code that I use to update my personal date variable in my projects to today's current date. This line works in Ubuntu's terminal, but the Mac terminal seems to be far behind. Unfortunately, I copied this snippet from some site, so I'm not sure how it exactly works. Suggestions?

grep -ilr --exclude=revar.sh --exclude=README.md "[DATE]" * |
    grep -v .git | xargs -i@ sed -i "s/\[DATE\]/${today}/g" @
1
  • @Migrators: This is a programming question, using shell scripting as the language (and for the purpose of updating source code files). It could be appropriate for both SO and SU, but since it was asked on SO, why migrate it?
    – Roger Pate
    Apr 18, 2010 at 1:37

5 Answers 5

4

How it exactly works

  1. Generate a list of file names:

    grep -ilr --exclude=revar.sh --exclude=README.md "[DATE]" *
    

    Search recursively (-r) for files and list (-l) the names of those where the contents of the file match case-insensitively (-i) the regex '[DATE]' (which means that the file contains any one of the 8 characters "AaDdEeTt"); exclude the names revar.sh and README.md, and

  2. Remove any files names from the list that contain a character followed by 'git' (so the file name 'agitator' will be removed):

    grep -v .git
    
  3. Process the files one at a time, applying a specific 'sed' script to the file. In the modern notation (POSIX - and MacOS X, and Linux, AIX, HP-UX, Solaris, etc):

    xargs -I@ sed -i "s/\[DATE\]/${today}/g" @
    

    The '-i' option to 'sed' means overwrite the input file(s) after processing. This is a GNU and BSD extension to 'sed' not specified in the POSIX standard.

What is implausible about it

This script can be criticized at numerous levels.

  • The first search string is incorrect; it should have backslashes before the brackets; most files contain at least one of the letters 'dateDATE'.
  • The second grep is presumably meant to remove anything under the '.git' directories and needs fixing.
  • The 'sed' command is not case-insensitive when looking for '[DATE]', unlike the first 'grep'.

How to Fix it

So, one of two alternatives makes sense.

Either:

grep -ilr --exclude=revar.sh --exclude=README.md "\[DATE\]" * |
grep -v '\.git/' |
xargs sed -i "s/\[[Dd][Aa][Tt][Ee]\]/${today}/g"

Or:

grep -lr --exclude=revar.sh --exclude=README.md "\[DATE\]" * |
grep -v '\.git/' |
xargs sed -i "s/\[DATE\]/${today}/g"

As Donal Fellows noted, there is no need in this context to use the '-i' or '-I' option to 'xargs'.

Is it useful even when fixed?

This just leaves me puzzled as to how it useful. On the first day, all occurrences of '[DATE]' are mapped to '2010-04-17'; what happens on the next day? How do you unmap the dates before you commit to the git repository?

Still, at least you now know what it does and how it does it.

3
  • I was assuming some sort of reporting. Sometimes it doesn't pay to probe too deep...
    – Donal Fellows
    Apr 17, 2010 at 23:15
  • 1
    Like @Donal says, you really don't want to know why I need this. Your second example is actually what I was looking for, thanks for the thorough explanation. Curious though, It's not passing in stdin into the sed command, it's just passing the file names in the sed command. Any idea? (I'm really disappointed with the Mac term btw). Here's the error for reference: 'sed: 1: "changelog": command c expects \ followed by text'
    – Corey hart
    Apr 18, 2010 at 5:09
  • @Corey: Is '${today}' something like '17/04/2010'? If so, you need to change the 's///' notation to 's%%%' instead - using the % or another character that does not appear in either pattern to separate the parts of the substitute operation. I'm not sure how you get 'sed' thinking there's a change command in the mix. You're right; xargs fixes things so that the executed command has /dev/null for its input, and the file names to be operated on as arguments. What's wrong with the Mac term that isn't fixed by switching from tcsh to a sane shell? And which do you prefer on the other system? Apr 18, 2010 at 5:36
6

BSD xargs does not have -i, and that option is deprecated in GNU xargs anyways (when was this guide written? 1995?). Use -I instead.

2
  • Thanks, there's only so much I can learn in a day so I rely heavily on what I read on the internet. Downfall is old articles I guess.
    – Corey hart
    Apr 18, 2010 at 5:10
  • Yeah, in mac should use -I, e.g. redis-cli keys 'foo*' | xargs -I@ echo @
    – zhuguowei
    Jun 23, 2016 at 13:07
3

The -i option to xargs is never needed when the place to perform the replacement is at the end of the string; that's where the filenames would be put anyway. And BSD xargs (which is what OSX provides) doesn't support that option either. So try this:

grep -ilr --exclude=revar.sh --exclude=README.md "[DATE]" * |
    grep -v .git | xargs sed -i "s/\[DATE\]/${today}/g"
1

As for the -i flag to sed on Mac OS X to edit files in-place you have to add an empty string as argument:

sed -i "" 's/regex/replace/g' file

sed -i "" 's/regex/'"${var}"'/g' file   # create sed expression using string concatenation
1
  • Or an extension to use for creating the name of a backup file. sed -i ".BAK" 's/regex/replace/g' file would create a backup file named "file.BAK". Apr 18, 2010 at 13:07
0

Using Bash parameter expansion you may also avoid sed formatting issues almost automagically:

# some basic Bash parameter expansion examples to play with
var='ddd/abc/ddd'
var="$(date)"
var=$'\\\n'

echo abc | sed 's/b/'"${var}"'/g'
echo abc | sed 's/b/'"${var//\//\\/}"'/g'

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .