Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

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" @
share|improve this question

migrated from stackoverflow.com Apr 18 '10 at 12:14

This question came from our site for professional and enthusiast programmers.

    
@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 '10 at 1:37

5 Answers 5

up vote 3 down vote accepted

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.

share|improve this answer
    
I was assuming some sort of reporting. Sometimes it doesn't pay to probe too deep... –  Donal Fellows Apr 17 '10 at 23:15
    
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 '10 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? –  Jonathan Leffler Apr 18 '10 at 5:36

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

share|improve this answer
    
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 '10 at 5:10

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"
share|improve this answer

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
share|improve this answer
    
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". –  Dennis Williamson Apr 18 '10 at 13:07

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'
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.