#include <bits/stdc++.h>
       
using namespace std;
typedef long long ll;
template<class T>bool chmax(T &a, const T &b) { if (a<b) { a=b; return 1; } return 0; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a=b; return 1; } return 0; }
       

       

class IndexedEdge {
 public:
  int from, to, index;
  bool reversed;
  IndexedEdge(int from, int to, int index, bool reversed = false)
      : from(from), to(to), index(index), reversed(reversed) {}

  IndexedEdge reverse() const {
    return IndexedEdge(to, from, index, !reversed);
  }
};
       
       

template <typename T>
struct WeightedEdge {
  int from, to;
  T _weight;
  WeightedEdge(){};
  WeightedEdge(int from, int to, T weight)
      : from(from), to(to), _weight(weight) {}

  void scanFrom(std::istream& strm, int base = 1) {
    strm >> from >> to >> _weight;
    if (base == 1) --from, --to;
  }

  T weight() const { return _weight; }

  WeightedEdge reverse() const { return WeightedEdge(to, from, _weight); }
};

template <typename T>
inline std::istream& operator>>(std::istream& strm, WeightedEdge<T>& t) {
  t.scanFrom(strm);
  return strm;
}

template <typename Edge>
class Digraph {
  int _E, _V;
  vector<vector<Edge>> _adjacencyList;

 public:
  int E() const { return _E; }

  int V() const { return _V; }
  int size() const { return _V; }

  Digraph() : _V(0), _E(0){};
  Digraph(int V, int E = 0) : _V(V), _E(E) { _adjacencyList.resize(V); }

  void addEdge(const Edge& e) {
    _adjacencyList[e.from].push_back(e);
    ++_E;
  }

  void addUndirectedEdge(const Edge& e) {
    _adjacencyList[e.from].push_back(e);
    _adjacencyList[e.to].push_back(e.reverse());
    ++_E;
  }

  void addNode() {
    ++_V;
    _adjacencyList.push_back(vector<Edge>());
  }

  void scanFrom(std::istream& strm, int base = 1) {
    if (_V == 0) strm >> _V;
    if (_E == 0) strm >> _E;
    _adjacencyList.resize(_V);
    for (int i = 0; i < _E; ++i) {
      Edge e;
      e.scanFrom(cin, base);
      assert(0 <= e.from < _V);
      assert(0 <= e.to < _V);
      addEdge(e);
    }
  }

  vector<Edge> operator[](int u) const { return _adjacencyList[u]; }
  vector<vector<Edge>>& adjacencyList() { return _adjacencyList; }
};

template <typename Edge>
inline std::istream& operator>>(std::istream& strm, Digraph<Edge>& t) {
  t.scanFrom(strm);
  return strm;
}

class Bridge {
  vector<int> depth, passingBackEdgeCount;
  vector<IndexedEdge> bridges;

  int dfs(int u, int p, int d, const Digraph<IndexedEdge>& G) {
    depth[u] = d;
    int up = 0, down = 0, prefixSum = 0;
    for (IndexedEdge& e : G[u]) {
      if (depth[e.to] != -1) {
        if (depth[e.to] < depth[u] - 1) ++up;
        if (depth[e.to] > depth[u] + 1) ++down;
        continue;
      }
      prefixSum += dfs(e.to, u, d + 1, G);
    }
    return passingBackEdgeCount[u] = up - down + prefixSum;
  }

 public:
  Bridge(const Digraph<IndexedEdge>& G) {
    const int V = G.V();
    depth.resize(V, -1);
    passingBackEdgeCount.resize(V, 0);

    for (int u = 0; u < (int)(V); ++u) if (depth[u] == -1) dfs(u, -1, 0, G);
    for (int u = 0; u < (int)(V); ++u) {
      if (passingBackEdgeCount[u] != 0) continue;

      for (IndexedEdge& e : G[u]) {
        if (depth[e.to] + 1 != depth[u]) continue;
        bridges.push_back(e);
      }
    }
  }

  vector<IndexedEdge> edges() { return bridges; }
};

int main() {
  int V, E;
  cin >> V >> E;

  Digraph<IndexedEdge> G(V, E);

  for (int i = 0; i < E; ++i) {
    int s, t;
    cin >> s >> t;
    IndexedEdge e(s, t, i);
    G.addUndirectedEdge(e);
  }

  Bridge bridge(G);

  vector<IndexedEdge> edges;
  for (auto& e : bridge.edges()) {
    edges.push_back(e.from < e.to ? e : e.reverse());
  };
  sort((edges).begin(), (edges).end(), [](const IndexedEdge& a, const IndexedEdge& b) {
    return a.from == b.from ? a.to < b.to : a.from < b.from;
  });
  for (const IndexedEdge& e : edges) {
    cout << e.from << " " << e.to << endl;
  }

  return 0;
}



Measured memory_bytes: 7268