Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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
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

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

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

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
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

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

You must log in to answer this question.

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