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.

In a linux shell, I want to make sure that a certain set of files all begin with <?, having that exact string and no other characters at the beginning. How can I grep or use some other to express "file begins with"?


Edit: I'm wildcarding this, and head doesn't give a filename on the same line, so when I grep it, I don't see the filname. Also, "^<?" doesn't seem to give the right results; basically I'm getting this:

$> head -1 * | grep "^<?"
<?
<?
<?
<?
<?
...

All of the files are actually good.

share|improve this question

9 Answers 9

up vote 7 down vote accepted

In Bash:

for file in *; do [[ "$(head -1 "$file")" =~ ^\<\? ]] || echo "$file"; done

Make sure they are files:

for file in *; do [ -f "$file" ] || continue; [[ "$(head -1 "$file")" =~ ^\<\? ]] || echo "$file"; done

share|improve this answer
    
and since we are all so pedantic: do not use the glob operator on huge amounts of filesnames, instead use find –  akira Mar 15 '10 at 13:01
    
using find can also return only plain files directly to start the pipe. –  mpez0 Mar 15 '10 at 13:40
1  
You can completely do it in Bash when using read instead of head, too: for file in *; do [ -f "$file" ] || continue; read < "$file"; [[ "$REPLY" =~ ^\<\? ]] || echo "$file"; done –  janmoesen Mar 17 '10 at 16:31
cat file.txt | head -1 | grep "^<?"

should do what you're asking for.

share|improve this answer
    
Yeah, but if I wildcard it, it doesn't give me filenames :( Also "^<?" didn't work for me, I used the -v switch. –  user13743 Mar 12 '10 at 19:27
2  
@Phoshi Compulsive cat usage, head -1 file.txt | grep "^<?" is enough. –  Benjamin Bannier Mar 12 '10 at 19:29
1  
Useless use of cat :-((( –  vwegert Mar 12 '10 at 19:32
    
Useless cat is useless :( –  user13743 Mar 12 '10 at 19:34
    
I find it's much simpler to remember commands if you keep everything modular and broken down. I -know- cat will work, I don't know if command will take the file as an argument. It might not be strictly necessary, but I'm not taking it out :) –  Phoshi Mar 12 '10 at 20:03

this:

  % for i in *; do head -1 $i | grep "^<?" ; echo "$i : $?"; done

gives you something like this:

  foo.xml: 0
  bla.txt: 1

every file not containing your pattern will be "marked" with "1". you can play with that until it fits your needs.

share|improve this answer
1  
You need to quote the file names if they could contain spaces. And you'd probably want to lose the output from 'grep' to /dev/null. You could also use: head -1 "$i" | grep '^<?' || echo "$i" which will only print the file name if it is problematic. –  Jonathan Leffler Mar 12 '10 at 22:37
2  
That is what "grep -q" is for. :-) –  janmoesen Mar 12 '10 at 23:05

Try this

for i in `find * | grep "php$"`; do echo -n $i " -> "; head -1 $i; done

This will get a list of every file ending in PHP, then loop thru it. echoing the file name and then printing the first line of the file. I just inserted

will give you output like:

calendar.php  -> <?php
error.php  -> <?php
events.php  -> <?php
gallery.php  ->
index.php  -> <?php
splash.php  -> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
information.php  -> <?php
location.php  -> <?php
menu.php  -> <?php
res.php  -> <?php
blah.php  -> <?php

then you can stick a normal grep at the end to get rid of what you want to see and find just exceptions

for i in `find * | grep "php$"`; do echo -n $i " -> "; head -1 $i; done | grep -v "<?php"

output:

gallery.php  ->
splash.php  -> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
share|improve this answer
4  
Useless use of grep; use "find -name '*.php'". Also, dangerous use of variables: use "find -exec your command here '{}' '+'" to avoid problems with "special" file names. Aside from that, always quote your variables: "head -1 "$i"", not "head -1 $i". –  janmoesen Mar 12 '10 at 23:04
    
for x in *.php;do echo $x \"head -n1 $x`\";done` –  user23307 Mar 13 '10 at 0:00

You can use awk for this:

$ cat test1
<?xxx>
111
222
333
$ cat test2
qqq
aaa
zzz
$ awk '/^<\?/{print "Starting with \"<?\":\t" ARGV[ARGIND]; nextfile} {print "Not starting with \"<?\":\t" ARGV[ARGIND]; nextfile}' *
Starting with "<?":     test1
Not starting with "<?": test2
$
share|improve this answer

Except for empty files, this Perl script seems to work:

perl -e 'while (<>) { print "$ARGV\n" unless m/^<\?/; close ARGV; }' *

I'm not immediately sure how to handle empty files; I'd be tempted to treat them as a separate special case:

find . -type f -size +0 -print0 |
    xargs -0 perl -e 'while (<>) { print "$ARGV\n" unless m/^<\?/; close ARGV; }'
share|improve this answer

Do the grep:

$ head -n 1 * | grep -B1 "^<?"
==> foo <==
<?
--
==> bar <==
<?
--
==> baz <==
<?

Parse out the filenames:

$ head -n 1 * | grep -B1 "^<?" | sed -n 's/^==> \(.*\) <==$/\1/p'
foo
bar
baz
share|improve this answer

Bash 4.0

#!/bin/bash
shopt -s globstar
for php file in /path/**/*.php
do
   exec 4<"$php";read line <&4;exec 4<&-
   case "$line" in
     "<?"*) echo "found: $php"
   esac

done
share|improve this answer

Let me have a go at this

find -type f | awk '
{
 if(getline ret < $0){
  if(ret~"^<\\?$"){
   print "Good["$0"]["ret"]";
  }else{
   print "Fail["$0"]";
  };
 }else{
  print "empty["$0"]";
 };
 close($0);
}'

nobody said wak was not available :-)

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.