Initial commit: psh - minimal bash-like shell with && || operators and tab completion
这个提交包含在:
@@ -0,0 +1,27 @@
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -std=c99 -O2
|
||||
LDFLAGS =
|
||||
|
||||
TARGET = psh.exe
|
||||
SRC = psh.c
|
||||
|
||||
.PHONY: all clean test
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(SRC)
|
||||
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
|
||||
|
||||
test: $(TARGET)
|
||||
@echo "Testing psh..."
|
||||
./$(TARGET) -c "echo hello"
|
||||
./$(TARGET) -c "pwd"
|
||||
./$(TARGET) -c "dir | findstr .exe"
|
||||
@echo "Tests passed!"
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET) *.o
|
||||
|
||||
# Build with readline support (if available)
|
||||
readline: $(SRC)
|
||||
$(CC) $(CFLAGS) -DHAVE_READLINE -lreadline -o $(TARGET) $(SRC)
|
||||
@@ -0,0 +1,115 @@
|
||||
# psh - A minimal bash-like shell in pure C
|
||||
|
||||
A lightweight POSIX shell implementation featuring:
|
||||
|
||||
- Command execution via fork/exec (Windows: CreateProcess)
|
||||
- Built-in commands: `cd`, `pwd`, `echo`, `exit`, `help`, `env`
|
||||
- Input/output redirection (`<`, `>`, `>>`)
|
||||
- Piping (`|`)
|
||||
- Background execution (`&`)
|
||||
- Command history with up/down arrows
|
||||
- Tab completion
|
||||
- `&&` and `||` operators
|
||||
- Basic environment variable support
|
||||
- Tilde expansion (`~`)
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
make
|
||||
```
|
||||
|
||||
Requires:
|
||||
- GCC or compatible C compiler
|
||||
- GNU Readline library (for line editing)
|
||||
|
||||
## Running
|
||||
|
||||
```bash
|
||||
./psh
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
### Built-ins
|
||||
- `cd [dir]` - Change directory
|
||||
- `pwd` - Print working directory
|
||||
- `echo [args]` - Print text (supports `$VAR` expansion)
|
||||
- `exit` - Exit shell
|
||||
- `help` - Show help
|
||||
- `env [var=val]` - Show or set environment variables
|
||||
- `history` - Show command history
|
||||
|
||||
### Redirection
|
||||
```bash
|
||||
ls > output.txt # Redirect output
|
||||
cat < input.txt # Redirect input
|
||||
echo hi >> log.txt # Append output
|
||||
```
|
||||
|
||||
### Piping
|
||||
```bash
|
||||
ps aux | grep python | wc -l
|
||||
```
|
||||
|
||||
### Background
|
||||
```bash
|
||||
sleep 100 &
|
||||
```
|
||||
|
||||
### Logical Operators
|
||||
```bash
|
||||
cmd1 && cmd2 # Execute cmd2 only if cmd1 succeeds
|
||||
cmd1 || cmd2 # Execute cmd2 only if cmd1 fails
|
||||
```
|
||||
|
||||
### Tab Completion
|
||||
Press `Tab` to complete commands:
|
||||
- Built-in commands (cd, pwd, echo, etc.)
|
||||
- Executable files in PATH and current directory
|
||||
- Environment variables
|
||||
|
||||
### Command History
|
||||
- ↑/↓ arrows: Navigate history
|
||||
- Tab: Try completion
|
||||
- Backspace: Delete character
|
||||
|
||||
## Example Session
|
||||
|
||||
```
|
||||
user@localhost:F:\Paze SH> echo Hello World
|
||||
Hello World
|
||||
|
||||
user@localhost:F:\Paze SH> ls && echo "List completed"
|
||||
psh.c README.md Makefile
|
||||
List completed
|
||||
|
||||
user@localhost:F:\Paze SH> false || echo "Fallback executed"
|
||||
Fallback executed
|
||||
|
||||
user@localhost:F:\Paze SH> help
|
||||
psh - minimal bash-like shell (Windows)
|
||||
|
||||
Built-in commands:
|
||||
cd [dir] Change directory
|
||||
pwd Print working directory
|
||||
echo [args] Print arguments
|
||||
exit Exit shell
|
||||
env [var=val] Show/set environment
|
||||
help Show this help
|
||||
|
||||
Features:
|
||||
cmd1 | cmd2 Pipe output
|
||||
cmd > file Redirect output
|
||||
cmd >> file Append output
|
||||
cmd < file Redirect input
|
||||
cmd & Run in background
|
||||
cmd1 && cmd2 Execute cmd2 if cmd1 succeeds
|
||||
cmd1 || cmd2 Execute cmd2 if cmd1 fails
|
||||
history View command history
|
||||
Tab Try command completion
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* psh - Test cases for the shell
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
|
||||
/* Test helper */
|
||||
int run_test(const char *cmd, const char *expected_substring) {
|
||||
char output[4096];
|
||||
FILE *fp;
|
||||
int passed = 0;
|
||||
|
||||
printf("Testing: %s\n", cmd);
|
||||
|
||||
/* Run command and capture output */
|
||||
char full_cmd[4096];
|
||||
snprintf(full_cmd, sizeof(full_cmd), "psh.exe -c \"%s\" 2>&1", cmd);
|
||||
|
||||
fp = _popen(full_cmd, "r");
|
||||
if (fp == NULL) {
|
||||
printf(" FAILED: Could not execute command\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(output, 0, sizeof(output));
|
||||
if (fread(output, 1, sizeof(output) - 1, fp) > 0) {
|
||||
/* Remove trailing newline */
|
||||
output[strcspn(output, "\n")] = '\0';
|
||||
|
||||
if (strstr(output, expected_substring) != NULL) {
|
||||
printf(" PASSED: Got expected output containing '%s'\n", expected_substring);
|
||||
passed = 1;
|
||||
} else {
|
||||
printf(" FAILED: Expected output containing '%s', got: '%s'\n", expected_substring, output);
|
||||
}
|
||||
} else {
|
||||
printf(" FAILED: No output from command\n");
|
||||
}
|
||||
|
||||
_pclose(fp);
|
||||
return passed;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int passed = 0;
|
||||
int total = 0;
|
||||
|
||||
printf("=== psh Test Suite ===\n\n");
|
||||
|
||||
/* Test 1: echo */
|
||||
total++;
|
||||
if (run_test("echo hello", "hello")) passed++;
|
||||
|
||||
/* Test 2: pwd */
|
||||
total++;
|
||||
if (run_test("pwd", "F:\\Paze SH")) passed++;
|
||||
|
||||
/* Test 3: environment variables */
|
||||
total++;
|
||||
if (run_test("echo $HOME", "")) passed++; /* Just check it doesn't crash */
|
||||
|
||||
/* Test 4: help */
|
||||
total++;
|
||||
if (run_test("help", "Built-in commands")) passed++;
|
||||
|
||||
/* Test 5: history (empty) */
|
||||
total++;
|
||||
if (run_test("history", "history")) passed++;
|
||||
|
||||
/* Test 6: && operator */
|
||||
total++;
|
||||
if (run_test("echo first && echo second", "first")) passed++;
|
||||
|
||||
/* Test 7: || operator */
|
||||
total++;
|
||||
if (run_test("false || echo fallback", "fallback")) passed++;
|
||||
|
||||
printf("\n=== Results: %d/%d tests passed ===\n", passed, total);
|
||||
|
||||
return (passed == total) ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
test1: echo Hello World
|
||||
test2: pwd
|
||||
test3: ls | findstr .exe
|
||||
test4: echo $HOME
|
||||
test5: mkdir testdir && cd testdir && pwd
|
||||
test6: false || echo "This should print"
|
||||
test7: true && echo "This should also print"
|
||||
test8: env
|
||||
在新工单中引用
屏蔽一个用户