class Linguist::Shebang
def self.call(blob, candidates)
blob's shebang is valid. Returns an empty list if there is no shebang.
Returns an array of languages from the candidate list for which the
Shebang.call(FileBlob.new("path/to/file"))
Examples
candidates - A list of candidate languages.
blob - An object that quacks like a blob.
Public: Use shebang to detect language of the blob.
def self.call(blob, candidates) return [] if blob.symlink? languages = Language.find_by_interpreter interpreter(blob.data) candidates.any? ? candidates & languages : languages end
def self.interpreter(data)
Public: Get the interpreter from the shebang
def self.interpreter(data) # First line must start with #! return unless data.start_with?("#!") shebang = data[0, data.index($/) || data.length] s = StringScanner.new(shebang) # There was nothing after the #! return unless path = s.scan(/^#!\s*\S+/) # Keep going script = path.split('/').last # if /usr/bin/env type shebang then walk the string if script == 'env' s.scan(/\s+/) while s.scan(/((-[i0uCSv]*|--\S+)\s+)+/) || # skip over optional arguments e.g. -vS s.scan(/(\S+=\S+\s+)+/) # skip over variable arguments e.g. foo=bar # do nothing end script = s.scan(/\S+/) end # Interpreter was /usr/bin/env with no arguments return unless script # "python2.6" -> "python2" script.sub!(/(\.\d+)$/, '') # #! perl -> perl script.sub!(/^#!\s*/, '') # Check for multiline shebang hacks that call `exec` if script == 'sh' && data.lines.first(5).any? { |l| l.match(/exec (\w+)[\s"']+\$0[\s"']+\$@/) } script = $1 end # osascript can be called with an optional `-l <language>` argument, which may not be a language with an interpreter. # In this case, return and rely on the subsequent strategies to determine the language. if script == 'osascript' return if s.scan_until(/\-l\s?/) end File.basename(script) end