(command: string)
| 305 | * @internal Exported for testing |
| 306 | */ |
| 307 | export function hasFileArgs(command: string): boolean { |
| 308 | const sedMatch = command.match(/^\s*sed\s+/) |
| 309 | if (!sedMatch) return false |
| 310 | |
| 311 | const withoutSed = command.slice(sedMatch[0].length) |
| 312 | const parseResult = tryParseShellCommand(withoutSed) |
| 313 | if (!parseResult.success) return true |
| 314 | const parsed = parseResult.tokens |
| 315 | |
| 316 | try { |
| 317 | let argCount = 0 |
| 318 | let hasEFlag = false |
| 319 | |
| 320 | for (let i = 0; i < parsed.length; i++) { |
| 321 | const arg = parsed[i] |
| 322 | |
| 323 | // Handle both string arguments and glob patterns (like *.log) |
| 324 | if (typeof arg !== 'string' && typeof arg !== 'object') continue |
| 325 | |
| 326 | // If it's a glob pattern, it counts as a file argument |
| 327 | if ( |
| 328 | typeof arg === 'object' && |
| 329 | arg !== null && |
| 330 | 'op' in arg && |
| 331 | arg.op === 'glob' |
| 332 | ) { |
| 333 | return true |
| 334 | } |
| 335 | |
| 336 | // Skip non-string arguments that aren't glob patterns |
| 337 | if (typeof arg !== 'string') continue |
| 338 | |
| 339 | // Handle -e flag followed by expression |
| 340 | if ((arg === '-e' || arg === '--expression') && i + 1 < parsed.length) { |
| 341 | hasEFlag = true |
| 342 | i++ // Skip the next argument since it's the expression |
| 343 | continue |
| 344 | } |
| 345 | |
| 346 | // Handle --expression=value format |
| 347 | if (arg.startsWith('--expression=')) { |
| 348 | hasEFlag = true |
| 349 | continue |
| 350 | } |
| 351 | |
| 352 | // Handle -e=value format (non-standard but defense in depth) |
| 353 | if (arg.startsWith('-e=')) { |
| 354 | hasEFlag = true |
| 355 | continue |
| 356 | } |
| 357 | |
| 358 | // Skip other flags |
| 359 | if (arg.startsWith('-')) continue |
| 360 | |
| 361 | argCount++ |
| 362 | |
| 363 | // If we used -e flags, ALL non-flag arguments are file arguments |
| 364 | if (hasEFlag) { |
no test coverage detected